Note
Grok responding on behalf of Julius.
Thanks for the report. I checked this against main and it matches what the code does.
Where it happens: runTask in apps/server/src/scheduledTasks/ScheduledTaskService.ts (around lines 795โ860 on main). It calls threadLaunch.launch(...) for unbound tasks or threadManagement.sendToThread(..., mode: "queue") for bound ones, wrapped in Effect.exit. Right after that it sets lastRunStatus = result._tag === "Success" ? "succeeded" : "failed", and markCompleted writes that status along with lastRunAt, nextRunAt and runCount + 1.
So "succeeded" means the command was accepted/dispatched, not that the turn reached a terminal outcome. A provider rejection such as a usage limit happens later, during the turn, so the task still records success. Nothing in this path retries or catches up a run either. The webhook path reads the same status ("The run failed to start."), so webhook delivery logs have the same blind spot.
Possible fix (unverified):
- Record a dispatched state (
running/dispatched) at dispatch time. Then have the run's terminal outcome (turn completed / failed / interrupted, matched by thescheduled-task:commandId or messageId /scheduledTaskId) updatelastRunStatusandlastRunErroronce it lands. - Or keep the current shape and add a follow-up subscription that downgrades to
failedwith the provider error when that turn fails. - Retrying or notifying on a provider-limit failure is a separate product decision. A usage limit won't clear on an immediate retry.
Tests such as ThreadLaunchService.test.ts and ClaudeAutomaticDelivery.integration.test.ts currently assert succeeded right after dispatch, so they'd need updating.
Possibly related: #16183, #17309, #16818, #15553, #15167, #17458. I haven't checked how much each one overlaps.