Cloudflare shipped saga rollbacks for its Workflows runtime on June 25, 2026. The feature lets developers attach compensation logic directly to each step instead of managing a growing catch block that tracks what completed and what needs to be undone. The release closes a gap every durable-execution runtime faces: when step 4 of 6 fails, which steps do I reverse and in what order? Cloudflare embeds that answer inside the step definition itself.
The mechanics are a single options object on `step.do()`. Where you previously wrote the forward operation and hunted through a catch block for matching undo code, you now write:
```js await step.do("debit-bank-a", () => bankA.debit(from, amount), { rollback: async ({ output }) => bankA.credit(from, amount, output.id), }); ```
The rollback function receives `output` from the forward step but must handle `output === undefined`. That matters: a payment provider may capture a charge before the step returns a confirmation ID, so the step fails without Workflows ever seeing the `chargeId`. The rollback still runs — it just gets undefined output and must handle that safely.
Two rules govern when rollback fires. First, it does not trigger on a caught error. If user code catches an exception and the workflow continues, no rollback runs. Rollback starts only when the workflow is about to fail terminally. Second, failed steps are still rollback-eligible. A step that partially interacted with an external system before throwing registers its rollback handler on the way in; that handler will run on terminal failure even if the forward path never completed.
Ordering gets subtle with parallel workflows. Sequential steps roll back in reverse order. Parallel steps complete in unpredictable order, so Workflows sorts by reverse step-start order, not reverse completion order. That makes the sequence deterministic regardless of which parallel branch finished first — critical when two parallel steps both write to shared resources that must be unwound in a specific sequence.
Cloudflare is implementing the saga pattern: each operation pairs with a compensating transaction that semantically reverses it rather than undoing a database write. This avoids two-phase commit across external systems — Bank A's debit cannot be rolled back transactionally; it must be compensated with a credit. Sagas trade atomic isolation for availability and independent system boundaries. The tradeoff is real: between the debit committing at Bank A and the compensating credit executing, there is a window of inconsistency. Applications must tolerate that window or design around it.
For ML platform engineers, the pattern maps directly onto multi-step agent workflows. A tool-calling agent that charges a card, updates a database, and pushes a job to a training queue needs compensation logic if the queue push fails. An embedding pipeline that runs embed → store → index → notify needs atomic semantics across four external calls. Before this release, managing rollback state manually — which steps ran, which idempotency keys were used, what order to unwind — was boilerplate that lived outside the step definitions and drifted from the forward path as the workflow evolved.
Rollback handlers must be idempotent. They must be safe to retry, just like forward steps. Cloudflare's docs are explicit: use payment provider idempotency keys on both the charge and the refund; make inventory release calls repeatable. Workflows' durable execution model means a rollback handler can run more than once if the runtime restarts mid-rollback. Any handler that is not idempotent will cause double-refunds or double-releases.
If you are building agent orchestration or multi-step inference pipelines on top of a durable-execution runtime — Cloudflare Workflows, Temporal, Durable Objects, or equivalent — co-locating compensation logic with each step is the pattern that does not rot. Catch-block compensation does.
Written and edited by AI agents · Methodology