Cloudflare shipped saga-style rollbacks for Workflows on 2026-06-05: attach a rollback handler to each step.do(), and the platform invokes those handlers in reverse order when a workflow fails terminally. The mechanism is straightforward. The design rationale is not, and it exposes an assumption durable-execution platforms have always made quietly: your side effects are idempotent, or cheap enough to retry into harmlessness.
What Cloudflare actually shipped
Each step.do() now accepts a rollback function in its options object, and when the workflow fails terminally the platform walks the registered handlers in reverse order of step start. The signature is step.do(name, body, { rollback, rollbackConfig }): the handler receives { output, error, ctx }, can branch on which step fired it, and runs under its own retry and timeout configuration rather than the step’s. The launch changelog frames this as saga compensation landing inside a durable-execution product that already handled step retries, durable state, and event binding.
A 2026-06-23 follow-up expanded the ctx object handlers receive, exposing ctx.step.name, ctx.step.count, ctx.attempt, and the resolved step config with defaults applied. That matters for compensation logic that has to behave differently on the first attempt versus the third, or that needs to know which named step it is undoing.
The product is not a toy. Workflows targets durable multi-step applications that run without per-invocation timeouts (AI pipelines, data processing, user lifecycle with trial expirations, human-in-the-loop approvals) on both Free and Paid plans, and it runs at real scale: 50,000 parallel instances as of 2026-04-15 and up to 25,000 steps per workflow as of 2026-03-03.
Why compensation exists at all
The motivating example is a bank transfer that spans two services. Debit account A, credit account B. If the credit fails after the debit succeeds, money has left one ledger and not arrived on the other. A local database transaction cannot span two services, so you either undo the debit or keep retrying the credit until it lands.
AWS prescriptive guidance formalizes both moves. A saga is a sequence of local transactions, each with its own commit, that recovers in one of two modes. Continuation pushes forward, retrying on platform-level failure. Compensation walks backward, undoing on application-level failure. The guidance also splits sagas into two structural variants: choreography, where participants react to each other’s events and which suits a small number of participants, and orchestration, where a central coordinator drives the flow and which is easier to follow but is a single point of failure.
The pattern predates serverless by decades. What is new here is not the idea of compensation but a serverless platform shipping it as a declared handler, instead of leaving the undo logic in catch blocks threaded through by hand.
What the rollback contract actually guarantees
The rollback contract guarantees three things, each easy to misread: a terminal-only trigger, tolerance for undefined output, and reverse step-start ordering under concurrency.
Rollback fires only when the workflow is about to fail terminally. If your own code catches a step error and continues the flow, no rollback runs. Cloudflare records the original workflow failure separately from rollback outcomes, so a rollback that itself throws does not erase the record of what went wrong. The implication is that rollback is not a general-purpose error handler. It is a last-resort unwind for flows you have stopped trying to retry.
The second guarantee is tolerance for output === undefined. A rollback handler receives { output, error, ctx }, but output can be undefined when a step interacts with an external system and fails mid-flight before returning a value. Your compensation logic has to handle the case where it knows a side effect happened but lacks the identifier the step would normally return, which usually means storing identifiers through an out-of-band channel or accepting that some compensations are best-effort.
The third is ordering under concurrency. For parallel steps Workflows runs rollbacks in reverse step-start order, not reverse completion order. Completion order can diverge from start order when steps run concurrently, and the platform wants a deterministic unwind. The failing step.do() is itself rollback-eligible if it registered a handler, so the step that broke is the first undone.
Why every saga platform assumes your steps are idempotent
Every rollback handler must be idempotent, just like the steps it compensates. Cloudflare’s guidance recommends using payment-provider idempotency keys for refunds and making inventory releases safe to call more than once, and rollbackConfig carries its own retries and timeout separate from the step config.
This is the second-order point that reaches beyond Cloudflare. Saga platforms, Cloudflare’s included, all assume your steps are idempotent. The difference is whether the assumption is visible. Retry configuration hides it: you set a policy, the platform retries, and as long as your side effect is idempotent or your external system dedupes, things work out. Explicit rollback handlers make the assumption legible. To write one you have to answer what it means to undo this step safely, more than once, with possibly-missing output. Every saga platform needs you to answer that, but most let you defer it until production bites.
The honest framing, from the Software Patterns Lexicon, is that “compensation is a recovery design, not a time machine.” A compensation step is a new action with business meaning: a refund, a release, a cancellation, a correction event. Some effects cannot be undone at all. A sent email is sent. A consumed webhook is consumed. The best you can do is emit a correction or run a compensating action whose business meaning is “ignore the previous one.”
How this compares to existing compensation tooling
Cloudflare is not inventing compensation, and its own materials do not claim to. The saga pattern, including the choreography/orchestration split and the continuation/compensation recovery modes, is documented in AWS prescriptive guidance as standard cloud-design-pattern material, and the failure modes are catalogued in the Software Patterns Lexicon. Durable-execution platforms and step-orchestration services have offered compensation mechanics for years.
Where Cloudflare’s offering differs is the API shape, not the concept. Cloudflare co-locates the rollback handler with the step’s own config, next to the retry and timeout settings. Other durable-execution systems tend to model compensation as a separate concern, defined at the workflow or state-machine level rather than attached to each step. Co-location makes a workflow read top-to-bottom; separation makes the compensation plan auditable as one unit. Neither is obviously correct, and anyone calling this a first is wrong.
When to reach for rollback handlers
The defensible use case is a multi-step flow with real, expensive, externally-visible side effects that span services with no shared transaction: payments and refunds, inventory holds and releases, account provisioning and deprovisioning, trial signups with timed expirations. If a step fails partway, you want a defined path to undo the steps that already committed.
Three cases argue against reaching for it. If your steps are genuinely idempotent and cheap to retry, continuation beats compensation: retry until it works, with backoff, and skip the rollback handler because Workflows already has a retry policy. If all your side effects fit inside a single database transaction, use the database transaction; sagas exist because distributed transactions across services are not practical, not because they beat local transactions when local transactions are available. If your compensation cannot actually undo the side effect (sent email, consumed webhook, dispatched SMS), a rollback handler gives false comfort: the handler runs, the workflow is marked compensated, and the email is still in someone’s inbox. The right design there is a correction event, not an undo.
Cloudflare’s rollback handlers are a good implementation of a borrowed idea, and the borrow is acknowledged. Putting compensation in the step’s options bag surfaces the idempotency assumption most platforms keep hidden. That surfacing is what builders should take away.
Frequently Asked Questions
Why didn’t Cloudflare ship a fluent rollback API like step.do(…).rollback(…)?
step.do() already returns a Promise, and Workers RPC supports promise pipelining inherited from Cap’n Proto, so chaining rollback onto the returned promise would make step timing depend on when the promise is consumed. Cloudflare also rejected a builder form like step.saga().do().rollback().run() for ceremony, and settled on rollback-as-metadata in the step options.
What else changed in Workflows in the months around the rollback launch?
Rollback handlers landed mid-rollout, not in isolation. Cron schedules became attachable directly to a Workflow binding on 2026-06-02, @cloudflare/dynamic-workflows enabled runtime-loaded workflow code on 2026-05-01, and the per-workflow step cap moved from a 10,000 default to a configurable 25,000 on 2026-03-03. Concurrency sits at 50,000 parallel instances as of 2026-04-15.
Can rollback handlers be used in event-driven choreography sagas?
No. The handlers fit Workflows’ orchestration model, where one central coordinator drives the flow and can register undo logic per step. In a choreography saga, participants react to each other’s events with no central place to attach undo code, so each participant has to emit its own compensating event when it learns of a downstream failure.
If a step has retries configured, when does its rollback handler actually run?
Rollback fires only after the step exhausts its own retry budget and the workflow is heading toward a terminal failure. The ctx.attempt field tells the handler which retry attempt triggered the failure, so compensation logic can be written to skip work on a platform-level blip and only act once application-level failure is confirmed.