The saga pattern: your automation's undo button is a forgery

Automation Engineering · By Caleb Sakala · June 22, 2026

A sly cartoon fox in a dim backroom hand-painting curved-arrow symbols onto stacks of oversized counterfeit undo buttons

A six-step automation runs cleanly for months. Then one Tuesday the fourth step times out and the run stops with a red error icon. The real problem is everything that already happened before it stopped. Steps one through three finished: a welcome email went out, a card was charged, a record was written to the CRM. The workflow halted, but none of that work rewound on its own. Handling that mess is the entire reason the saga pattern exists, and most people running multi-step automations are using a half-built version of it without knowing it has a name.

What the saga pattern fixes

The name comes from a 1987 database paper by Hector Garcia-Molina and Kenneth Salem. They were dealing with long-lived transactions, operations that would hold database locks far too long to commit atomically. Their idea was to break one long transaction into a sequence of smaller steps, T1 through Tn, and give each step a compensating transaction, C1 through Cn, that semantically undoes it. Finish every forward step and the saga is complete. Fail at step four and you run C3, then C2, then C1 to walk the system back to a consistent state.

A workflow you build in any automation tool has the same shape. Each step is a local transaction against some outside service. The platform hands you the forward path for free. It does not hand you the compensations, and that missing half is where the trouble lives.

Why compensation is not a rollback

The textbook diagrams gloss over the hard part. A database rollback erases history, as if the transaction never ran. A compensation cannot do that. It is a new transaction that runs forward to approximate the reverse of the one before it. If step one charged a card, the compensation is not an erasure of the charge. It is a refund, a separate event with its own timestamp, its own fees, and its own chance of failing.

Some steps have no honest compensation at all. You cannot unsend an email. The compensation for a welcome email is a second email asking the reader to ignore the first, which they have already opened. The undo button you build is closer to a forgery than the real thing. It convinces your own system that order was restored. The customer still has the email.

A worked example: mapping every step to its undo

Take an order automation with five steps. Writing each one beside its compensation, and marking whether that compensation truly reverses the effect, turns an abstract worry into a checklist:

Step                       Compensation                Real reversal?
1. Reserve inventory       Release the reservation     Yes
2. Charge the card         Issue a refund              Partial: fees are lost
3. Buy a shipping label    Void the label              Partial: only in a window
4. Email the confirmation  Send a correction email     No
5. Write the ledger entry  Post a reversing entry      Yes: audit trail intact

If step five fails, four compensations have to run. Two are clean. One costs money every time it fires. One is impossible in any real sense and can only be followed by an apology. Map your own workflow this way and you will almost always find a row that reads "No," and that row should change how you order the steps.

The window where the data is wrong

A saga gives up one of the four guarantees a database transaction provides: isolation. While the sequence is mid-flight, other processes can read data that is half-updated. Between charging the card at step two and writing the ledger entry at step five, a report that sums payments will see money the accounting system has not recorded yet. The classic fixes carry over from the original research: a semantic lock that flags a record as in-progress, or a version number that lets a later step detect it is working from a stale read. For most business automations, keeping the saga short and fast is enough, since it shrinks the window where the data is inconsistent.

Put the irreversible step last

Ordering is the cheapest defense you have. When a step can never be compensated, push it as late in the sequence as the logic permits, so everything fragile has already succeeded before the irreversible thing happens. Send the confirmation only once the payment has cleared and the label has printed. The steps most likely to fail then run while the system can still back itself out.

Why the saga pattern needs a single owner

When a step fails, something has to decide which compensations run and in what order. The two stock answers are orchestration, where one central component drives the forward steps and the rollbacks, and choreography, where each step emits an event and the next one reacts. Most saga write-ups pour their energy into this choice. For a small team automating business processes, it rarely matters much, and choreography is often a trap: chasing a failure that is scattered across five services all reacting to each other's events is far harder than reading one orchestrator's log.

There is a second reason to centralize. Compensations often touch sensitive operations like refunds and record deletions, and you do not want every step holding the authority to fire those. When a procurement workflow's payment step fails after the purchase order already went out, the reversal only runs cleanly if one component, and only one, is allowed to run it. That is the logic behind how Chase Agents routes a research step's purchase_recommendation objects to a separate approval step while keeping tool access on the orchestrator, so a failing step can request a compensation without being able to execute one itself. That puts the authority to undo in the workflow's structure, where a single owner controls it.

Telling a failure from a delay

Running a compensation against a step that secretly succeeded creates its own incident. Step four times out at fifteen seconds, so the workflow marks it failed and refunds the card. At sixteen seconds the original request lands and ships the order. Now there is a shipped order with no payment behind it. Two defenses keep this from happening. Compensations must be idempotent, safe to run twice, because retries and rollbacks will sometimes overlap. And the trigger has to be confident the step genuinely failed.

That confidence requires defining what success means for the step, which is more than the absence of an exception. Picture a step that returns HTTP 200 with an empty body. Was that a success or a quiet failure? Guess wrong and you either skip a compensation you needed or fire one at a step that half-worked. Declaring each step's expected output schema and success criteria up front, which Chase Agents validates on every run, is what lets a workflow answer that question before it decides to roll anything back.

Recent saga write-ups converge on hybrid recovery, and it is the right default. Retry a transient failure a few times with exponential backoff first, and only compensate once the retries are spent or the error is clearly permanent. A 503 from an overloaded service deserves a few attempts, while a 422 validation error will fail identically every time and should route straight to compensation. Compensation is expensive and often lossy, so it belongs at the end of the line, after retries have failed.

Read your longest workflow tonight

Open your longest automation and read it from the top. For every step, write the single sentence that says how you would undo it. The steps where that sentence becomes an apology, where no clean reversal exists, are the ones that will define your next bad incident. Move them as late as the logic allows, and build their compensations now, before you need them, not during the postmortem.