Runs, failures and Resume
What every run records, how a failing firing is retried and stopped, and how Run now and Resume carry it on.
Every attempt to run a job is recorded, including the ones that were refused. The failure worth guarding against is not a job that crashes loudly; it is one that quietly stops doing anything while its schedule keeps ticking. The run history is where that shows up as denials and failures instead of silence.
Open a job under Code › Scheduled jobs and look at Run history, or read it from code with
sdk.crons.listRuns.
What a run records
One run is one firing (or the part of one that a Resume carried on). It records:
- why it ran:
schedule,manual(Run now) orresume; - when: the instant the schedule meant (
scheduledFor), when it started and finished, and how many scheduled windows were skipped to get here; - as whom: the job's principal, and the roles it held at the moment it ran, not what it holds now;
- what ran: the handler and the published version that executed, and the payload;
- how it went: its status, the turns it took and the failed turns it retried, rows done and failed (with the first 100 failures), the handler's result, and the error text.
Statuses
| Status | Meaning |
|---|---|
running | In progress, possibly over many turns. |
succeeded | The firing completed: every row was handled, or the handler returned. |
partial | The firing completed, and some rows failed. They are listed on the run. |
failed | It could not complete: a turn failed four times, the handler has no published version, or it could not be run at all. |
stopped | It was paused with work left. Resume carries on. |
denied | Refused before it ran: the job's principal was deleted or deactivated. |
A failed or stopped firing keeps its place, so nothing it finished is repeated when it carries
on.
When a turn fails
A firing is carried out in turns. When one fails (the handler throws outside a row's function, times out, or cannot be reached), the platform:
Retries it from the same place
After 1 minute, then 5, then 15, each time from the position the last successful turn saved. The job shows the firing as retrying, with the last error.
Stops after the fourth failure
The run is closed as failed with Failed after 4 attempts: and the reason. The firing is kept,
stopped, at its last good position.
Tells the owner, once
The account's contact gets one email for that firing: which job stopped, the last error, and a link to the job. One email per stopped firing, never one per attempt.
Waits for you, or for the schedule
Resume carries the firing on from where it stopped. So does the job's next scheduled time: a stopped firing is finished before a new one begins.
Some failures are not retried, because trying again would give the same answer: a principal that
no longer exists or is inactive (denied), a handler that was deleted or never published
(failed). Fix the cause, then Resume.
A process that dies in the middle of a turn (a deploy, a crash) is not a failure of your code. Its claim on the job expires and the next turn carries on from the last saved position.
Run now and Resume
Run now fires the job immediately, for real, through the same path the scheduler uses and as the job's own principal. Rows written and mail sent are permanent: it is the counterpart of a handler dry run, not a safer version of it.
It runs as the job rather than as you on purpose: the mistake a test most needs to catch is "this job is missing a role", and a version that ran as you would sail straight through it.
When the job has an unfinished firing (running, retrying, or stopped), the button reads Resume and carries that firing on from where it is, never from the beginning.
- It takes up to 20 turns while you wait, then reports what it did.
- If work remains and the job is active, the rest carries on in the background.
- If the job is paused, it stops where it got to (
stopped), and the next Resume carries on. - It is refused with
CRON_BUSY(409) while a turn of the job is running anywhere: two turns of one firing never run side by side, or the same page would be handled twice.
A person's run never moves the schedule's health. The job's last status and failure count describe the schedule, so only a firing the schedule started changes them. Run now and Resume leave them, and the next scheduled time, alone.
Approvals inside a job
If the handler pauses on sdk.awaitApproval, then once the request is approved, the job's next run
(its schedule, or Run now) reaches the same call and passes the approved gate. See Approvals.
The failure count
consecutiveFailures counts scheduled firings that ended failed or denied in a row. It is shown
on the job and never switches a job off on its own: a broken job is surfaced, not silently
disarmed. Pause it yourself if it should hold.
Errors
| Code | When |
|---|---|
CRON_BUSY (409) | Run now or Resume while a turn of the job is running. Try again once it finishes. |
JOB_ITEM_TIMEOUT (504) | Recorded on a row that took longer than 60 seconds; the row is skipped. |
HANDLER_PLATFORM_ONLY (403) | Something other than the job tried to run its job-only handler. |
SDK_CAP_NOT_IN_MANIFEST (403) | The handler used a power its published version was not frozen with. |