---
title: "Undo failed multi-step work with · Agent Wranglers"
url: https://agentwranglers.se/playbook/events/consuming-events/sagas
id: EVT-15
level: Recommended
updated: 2026-10-27
build: a2a6bdfed830
tokens: 994
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Undo failed multi-step work with compensating steps

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Consuming events](https://agentwranglers.se/playbook/events/consuming-events.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-27.

- ID: EVT-15
- Level: Recommended
- Chapter: [Consuming events](https://agentwranglers.se/playbook/events/consuming-events.md)
- Rule: When one business action spans several services, write a compensating step for each step that can be undone. An order may reserve stock, book the courier and charge the card. Run them in reverse order when a later step fails.
- Check: For each saga, a test makes every step fail in turn. After each run, the state matches either a finished order or no order at all, with stock released and no charge left. A query finds no saga unfinished for more than its time limit.
- How: No database transaction spans three services, so a failure halfway leaves stock reserved for an order that was never paid. A saga runs each step as a local transaction and records its state. One coordinator can drive the steps, which is easier to follow, or each service can react to the last one's event. Mark the point of no return, such as the charge, and put it last where you can. Every step after it must be retryable rather than undoable. Each compensating step must be safe to run twice, as [Process each message once](https://agentwranglers.se/playbook/events/consuming-events/idempotent-consumers.md) says. A compensating step can fail too, so alert on a saga that stays unfinished past its time limit. Long workflows can run on a workflow engine, such as Temporal.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
const STEPS = [
  { name: 'reserve-stock', run: reserveStock, undo: releaseStock },
  { name: 'book-courier', run: bookCourier, undo: cancelCourier },
  { name: 'charge-card', run: chargeCard, undo: null },
];

export async function placeOrder(saga) {
  const done = [];
  try {
    for (const step of STEPS) { await step.run(saga); done.push(step); await saga.record(step.name, 'done'); }
  } catch (error) {
    for (const step of done.reverse()) if (step.undo) await step.undo(saga);
    await saga.record('order', 'compensated');
    throw error;
  }
}
```

### Python example

```python
STEPS = [
    ("reserve-stock", reserve_stock, release_stock),
    ("book-courier", book_courier, cancel_courier),
    ("charge-card", charge_card, None),
]

def place_order(saga) -> None:
    done = []
    try:
        for name, run, undo in STEPS:
            run(saga)
            done.append(undo)
            saga.record(name, "done")
    except Exception:
        for undo in reversed(done):
            if undo:
                undo(saga)
        saga.record("order", "compensated")
        raise
```

## Sources

- [Microsoft Azure Architecture Center, Saga pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/saga)

## Related rules

- EVT-11 [Process each message once](https://agentwranglers.se/playbook/events/consuming-events/idempotent-consumers.md) in Integration and events

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/events/consuming-events/sagas">Agent Wranglers integration and events playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Keep events long enough to replay them](https://agentwranglers.se/playbook/events/consuming-events/event-replay.md)
- [Consuming events](https://agentwranglers.se/playbook/events/consuming-events.md)
