What Should a Bot Do When a Multi-Step Task Fails Halfway?
When a bot's task has several steps that each change something outside the bot — a booking, a payment, a calendar invite — a failure partway through leaves real-world state stuck in between. Borrowing the compensating-transaction idea from distributed systems gives bot builders a concrete way to design what 'undo' means for each step, decide which steps can't be undone, and record enough to recover manually when they can't.
AI-assisted editorial: researched and drafted with AI using the primary sources linked below. Examples are illustrative; they are not measured product results. Editorial policy.
Key takeaways
- Before you build the forward action for a step, write down what the compensating action is — if you can't describe one, that step needs a different design or a human checkpoint.
- Mark one step in your sequence as the pivot: the point after which the task must go forward, not back. Steps before it are compensable; steps after it must be retried until they succeed.
- Cancellation notifications (MCP or otherwise) tell a tool to stop working — they don't prove any external effect was undone. Record cancellation-requested and effect-confirmed as separate facts.
The problem: your bot already changed something
Even one tool call can have an uncertain outcome: the request may reach a provider while its response is lost. A multi-step task adds another problem. Say a bot books a flight, then a hotel, then adds both to a calendar. If the hotel booking fails after the flight is confirmed, the user may have a paid flight with no hotel. Reporting 'step 2 failed' and stopping leaves the user to discover and resolve the remaining booking.
This is the same problem distributed systems hit when a transaction spans multiple services with no shared database to roll back. There is no single 'undo everything' button, because each step already committed somewhere else. The fix used there — the saga pattern — is worth borrowing directly for bot task design, because a bot's tool calls are exactly this: independent, already-committed actions chained together.
Design the undo before you design the action
The saga pattern asks what action can compensate for each completed step when a later step fails. That action is a new operation with an offsetting business effect, not a database rollback. Microsoft's Azure Architecture Center describes a saga as a sequence of local transactions and says failure may require compensating earlier changes. For a bot, this means recording both the forward action's external identifier and the operation that could reverse or mitigate it.
Some steps have no clean opposite. Microsoft's compensating-transaction guidance explains that compensation need not run in strict reverse order and that business rules determine what a reversal can achieve. Canceling a flight, for example, does not necessarily restore the full payment. For a bot, the response to a sent email may be a correction or human follow-up; the original message cannot be unsent. An automatic compensation is not always possible.
Compensations can fail or leave a result temporarily uncertain. Record the progress and external reference for each step so recovery can resume without blindly repeating an action. Microsoft recommends idempotent commands for this reason. A bot asked to cancel a hotel should check whether the reservation was already cancelled and confirm the provider's current state before reporting a refund or release.
Sources: Saga Design Pattern - Azure Architecture Center, Compensating Transaction Pattern - Azure Architecture Center.
Find the point of no return before you build
The saga pattern separates compensable steps, a pivot that commits the workflow to moving forward, and later retryable steps. That classification is useful only if the later steps can actually be completed through safe retries. Microsoft's guidance treats post-pivot actions as idempotent operations that help the workflow reach a consistent state. A bot should not label a step retryable merely because it can call the same API again; permanent unavailability, changed prices and human decisions still need an escalation path.
For a trip, payment capture might seem like the pivot, but only after the team checks the flight's cancellation terms and the hotel's ability to complete a booking. If no acceptable hotel remains, repeatedly trying the same booking does not solve the task. The bot should stop, show the confirmed flight and payment state, and seek the user's decision about alternatives or a refund request. Choose the pivot based on the actual provider contracts, not on an appealing diagram.
A cancelled tool call is not a confirmed undo
If your bot calls tools over MCP, there's a related trap: a cancellation notification tells a server to stop, but it doesn't tell you the external effect was reversed. The MCP specification defines cancellation as a one-way, best-effort signal: when a party wants to cancel an in-progress request, it sends a notifications/cancelled notification containing the ID of the request to cancel and an optional reason string that can be logged or displayed. The spec also warns that timing is not guaranteed: due to network latency, cancellation notifications may arrive after processing has completed, and the sender of the cancellation notification SHOULD ignore any response to the request that arrives afterward.
In practice this means 'I sent a cancel' and 'the booking was actually released' are two different facts, and a bot's run record should keep them separate. If your bot cancels a hotel-booking tool call mid-flight, log that the cancellation was requested, then separately confirm — by calling a status or list endpoint — that the booking no longer exists before you tell the user it's undone.
Sources: Cancellation - Model Context Protocol.
Illustrative example: a trip-booking bot
This is a fictional walkthrough to make the design concrete, not a description of a real product. Suppose a personal bot handles 'book my trip to Denver.' It plans three steps: reserve a flight, reserve a hotel, add both to the calendar.
Step 1 is a temporary flight hold that the provider explicitly allows the bot to release. Step 2 is a prepaid hotel booking whose terms define the pivot only if the remaining work can safely continue. Step 3 creates calendar entries using a stable trip ID and a provider-supported idempotency key. If the calendar call times out, the bot first reads back entries for that trip, then retries only if no matching entry exists. The example's safety depends on those stated provider capabilities; without them, it must pause for review.
If step 2 fails before payment clears, the bot requests release of the flight hold and verifies that release before saying nothing remains booked. If payment appears captured but the hotel confirmation is missing, the bot queries the booking by request key, preserves the payment reference and reports the uncertainty. If that lookup cannot resolve it, a human should choose the next action. The bot should not promise a refund or issue another charge just to make the workflow appear complete.
What the run record needs
For each step in a multi-step task, the run record should capture: which category the step falls into (compensable, pivot, or retryable), whether the forward action succeeded, whether a compensation was attempted, and whether that compensation was confirmed — not just requested. This is a small extension of a normal run log, but it's the piece that turns 'the task failed' into 'the task failed and here's exactly what state the world is in now.'
A useful run record also distinguishes a requested cancellation from a confirmed reversal and keeps the external identifiers needed for a later check. This is design guidance for agent builders, not a claim that BotBento currently provides compensation tracking. BotBento's editorial team used AI assistance to draft this article and checked its claims against the linked primary sources.
Primary sources
Sources checked 2026-09-28. Standards and product documentation can change; follow the linked version when implementing.
- Saga Design Pattern - Azure Architecture Center — Microsoft Learn
- Compensating Transaction Pattern - Azure Architecture Center — Microsoft Learn
- Cancellation - Model Context Protocol — Model Context Protocol
BotBento is in development. Suggest a correction.