Sleeping and retrying
This guide details how to sleep a Workflow and/or configure retries for a Workflow step.
You can set a Workflow to sleep as an explicit step, which can be useful when you want a Workflow to wait, schedule work ahead, or pause until an input or other external state is ready.
Use step.sleep
to have a Workflow sleep for a relative period of time:
await step.sleep("sleep for a bit", "1 hour")
The second argument to step.sleep
accepts both number
(seconds) or a human-readable format, such as “1 minute” or “26 hours”. The accepted units for step.sleep
when used this way are as follows:
| "second"| "minute"| "hour"| "day"| "week"| "month"| "year"
Use step.sleepUntil
to have a Workflow sleep to a specific Date
: this can be useful when you have a timestamp from another system or want to “schedule” work to occur at a specific time (e.g. Sunday, 9AM UTC).
// sleepUntil accepts a Date object as its second argumentconst workflowsLaunchDate = Date.parse("24 Oct 2024 13:00:00 UTC");await step.sleepUntil("sleep until X times out", workflowsLaunchDate)
You can also provide a Unix timestamp (seconds since the Unix epoch) directly to sleepUntil
.
Each call to step.do
in a Workflow accepts an optional StepConfig
, which allows you define the retry behaviour for that step.
If you do not provide your own retry configuration, Workflows applies the following defaults:
const defaultConfig: WorkflowStepConfig = { retries: { limit: 5, delay: 10000, backoff: 'exponential', }, timeout: '10 minutes',};
When providing your own StepConfig
, you can configure:
- The total number of attempts to make for a step
- The delay between attempts
- What backoff algorithm to apply between each attempt: any of
constant
,linear
, orexponential
- When to timeout (in duration) before considering the step as failed (including during a retry attempt)
For example, to limit a step to 10 retries and have it apply an exponential delay (starting at 10 seconds) between each attempt, you would pass the following configuration as an optional object to step.do
:
let someState = step.do("call an API", { retries: { limit: 10, // The total number of attempts delay: "10 seconds", // Delay between each retry backoff: "exponential" // Any of "constant" | "linear" | "exponential"; }, timeout: "30 minutes",}, async () => { /* Step code goes here /* }
You can also force a Workflow instance to fail and not retry by throwing a NonRetryableError
from within the step.
This can be useful when you detect a terminal (permanent) error from an upstream system (such as an authentication failure) or other errors where retrying would not help.
// Import the NonRetryableError definitionimport { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';import { NonRetryableError } from 'cloudflare:workflows';
// In your step code:export class MyWorkflow extends WorkflowEntrypoint<Env, Params> { async run(event: WorkflowEvent<Params>, step: WorkflowStep) { await step.do("some step", async () => { if !(event.data) { throw NonRetryableError("event.data did not contain the expected payload") } }) }}
The Workflow instance itself will fail immediately, no further steps will be invoked, and the Workflow will not be retried.