Development Choices

Compensating actions for multi-step AI agents

Author
Gregory MostizkySoftware Engineer
Published
Section
AI Agents
Length
14 min read3 sources cited
A precise mechanical sequence reversing its completed movements after one stage fails

Treat every agent side effect as a durable forward-and-undo record: save the result, compensation inputs, and authorization before continuing. Declare the point of no return, make everything after it retryable or manually resolvable, and run compensation as an idempotent, checkpointed workflow with explicit escalation when an undo cannot finish.

Compensation is part of the plan

A multi-step agent workflow reaches a failure and runs compensating actions before reconciliation
Recovery logic is designed with the forward path, not after an incident.

A compensating action is a domain-specific operation that brings a system back to a valid state after a later step fails; it is not a database rollback and might not restore the exact state that existed before the workflow began. A multi-step agent must therefore plan recovery alongside each side effect, before it has another opportunity to change the world.

The Microsoft Compensating Transaction pattern, last updated April 20, 2026, describes compensation as another eventually consistent workflow. It records how to undo completed work, applies business rules when concurrent work prevents a simple reversal, and can pause for a person when the correct outcome is ambiguous or high impact.

This matters more for an agent than for a request handler that performs one known mutation. An agent can choose tools and branch after seeing their results. If its recovery plan exists only in model context, a process restart, context loss, or handoff leaves the next worker without an authoritative account of what happened. Recovery state belongs in durable workflow data controlled by the application.

Record the undo before the next side effect

Record each completed side effect together with the inputs and authorization needed to compensate it before advancing to the next step. The ordering is the guarantee: the workflow may continue only after both the external result and its compensation record are durably represented.

A useful record identifies:

Do not record only a prose instruction such as remove the resource later. The compensator needs the exact target and the conditions under which changing it remains valid. A create operation normally produces a resource identifier; an access grant can produce a binding identifier; an update might require the version observed immediately after the write. Saving only the forward request can be insufficient because the service might assign the identifier or normalize the resulting state.

A prepared intent record written before a tool call is still useful, but it does not prove that the side effect completed. Keep the distinction explicit: prepared means the call may not have happened, started means its outcome may be unknown, and completed means the result and compensation data have been captured. An ambiguous timeout must remain unresolved until the workflow queries the target system or retries through an idempotent operation. Treating no response as no mutation is how duplicate resources and missing compensations enter the workflow.

The cost is at least one durable state transition around every side-effecting step, plus storage for receipts and compensation inputs. There is also implementation work in every tool adapter: it must turn a vendor-specific result into a stable compensation record. That cost is wasted for a read-only call, but it is part of the operation for any call that creates, updates, deletes, publishes, grants, charges, or sends.

Preserve compensation authority

Recording authorization does not mean copying an access token into every workflow row. Record which principal is allowed to compensate, the required scope, and how the compensation worker obtains an acceptable credential. Keep the credential itself in the system described by the site’s guide to credentials issued to an agent, with the workflow holding a reference rather than an exposed secret where possible.

The distinction matters because authorization can disappear before compensation runs. A short-lived credential can expire, a grant can be revoked, or the original agent session can end. A compensation design is incomplete if it names an undo operation but gives the recovery worker no permitted way to invoke it.

Do not silently substitute broader credentials when the recorded authority fails. The compensation might be mechanically possible while no longer authorized. Preserve the failed authorization state, stop that branch, and send it to the escalation path. Manual resolution can then approve a new principal, choose a different business action, or accept the remaining side effect with an audit record.

The required permission should be as specific as the action permits. A compensator that can revoke one recorded grant does not thereby need unrestricted access administration. This adds credential lifecycle and policy work, but it prevents recovery from becoming an unbounded administrative channel.

Mark the pivot explicitly

A pivot is the boundary after which the operation cannot be undone. The Microsoft Saga pattern, retrieved August 26, 2026, defines it as the point of no return: it can be the last compensable transaction or the first retryable transaction, and operations after it must carry the workflow to a consistent final state.

Mark that boundary in the workflow definition and persist when a run crosses it. Do not infer it later from which steps happen to have completed. Before the pivot, failure may select compensation. After the pivot, the orchestrator must not launch an obsolete rewind that pretends the irreversible effect never happened.

Place the pivot only after the validations and reversible preparation on which the irreversible action depends. If a workflow prepares a resource, grants access, and then publishes an instruction that cannot be recalled, the publication is at or beyond the pivot. Validation of the target, authorization, content, and prepared dependencies belongs before publication. Moving those checks later creates a state that no compensating action can erase.

A pivot is a semantic property, not simply the most expensive call. Sending a message, releasing a signed artifact, or transferring control to another organization might be irreversible even when the API call is small. Conversely, an expensive allocation can remain compensable if the domain provides an authorized cancellation that leaves a valid state.

Unknown outcome needs special handling at the pivot. If the connection drops while the irreversible operation is running, the orchestrator must establish whether it happened. Repeating a non-idempotent pivot can duplicate the effect, while compensating as though it did not happen can conflict with reality. The workflow therefore needs a status query, an idempotent contract, or manual reconciliation capable of deciding which state actually exists.

Everything after the pivot must finish

Later steps must be retryable or routed to manual resolution. There is no valid automatic path that abandons them and reports success, because the workflow has already crossed the point where rewind is available.

Retryable means more than putting a call in a loop. The operation must accept repeated delivery without producing additional side effects, retain enough state to recognize the same intent, and return a result the orchestrator can interpret consistently. Retries also need a finite policy. The site’s guide to retry budgets for agent tools covers the limit on automated attempts; exhausting that budget should change the workflow state to manual resolution rather than make the work disappear.

Manual resolution is a first-class terminal route, not an informal instruction to inspect logs. Persist what remains unfinished, why automatic progress stopped, which side effects already exist, and which operation a person is authorized to perform. The visible run status should distinguish completed, compensating, compensation blocked, post-pivot retrying, and awaiting manual resolution.

Compensation can fail too

Compensation is itself a failure-prone workflow, so every undo action needs idempotency, progress checkpoints, and an escalation path. Network failures, unavailable services, rejected authorization, concurrent changes, and domain rules can interrupt recovery just as they interrupt forward work.

An undo should consequently have its own command, state, attempts, and result. Do not implement compensation as a best-effort exception handler tied to the lifetime of the failed process. Once a completed side effect exists, responsibility for resolving it must survive worker termination and orchestration restarts.

Compensation also need not apply actions in the exact reverse of forward execution. Dependencies and business risk determine the order. A later step may be independent and safe to undo in parallel, while another undo must wait until a dependent grant or reservation has been removed. Store those constraints in the workflow rather than asking a recovering model to reconstruct them from a transcript.

Nor does compensation necessarily restore an earlier snapshot. Another actor may have changed the resource since the forward step. A compensating command must account for the current state and preserve unrelated concurrent work. Conditional updates, version checks, and domain operations such as cancel or revoke express that intent more accurately than overwriting the resource with an old copy. When the precondition no longer holds, stopping for review is safer than forcing the old state over newer work.

Make each undo idempotent

The AWS Builders’ Library guidance on idempotent APIs, retrieved August 26, 2026, recommends a unique caller-provided request identifier. The service associates repeated requests from the same caller and identifier with the same intent, records the identifier atomically with the mutation, and rejects reuse of the identifier with different parameters.

Apply that contract to compensation, not just to forward actions. Derive or allocate a stable key for one workflow step and one compensation intent, then reuse it for every attempt. Do not generate a new key on each retry: that tells the target service that every delivery is new work.

The target service should store the key and original parameters with the mutation as one atomic operation. Otherwise it can perform the undo but lose the key, allowing a retry to repeat the side effect, or store the key without performing the undo, causing later retries to be suppressed. The same key with changed parameters should fail validation and enter investigation; it should not silently redefine what the compensation means.

Return a semantically equivalent result for a repeated key when the service supports it. The compensator then receives the same meaning whether the first response arrived, was lost after success, or came from a later replay. A bare already absent response might be acceptable for a delete, but only if the workflow can establish that the absence represents its compensation rather than an unrelated actor’s change.

Retain idempotency records for the full period in which a delayed or replayed compensation can arrive. AWS notes that retention requirements vary by service and resource; its EC2 example ties the period to the resource lifetime plus an interval for late requests. There is no supplied universal duration, so the workflow must define retention from its own replay and resource lifecycle rather than inventing a default.

The detailed design belongs with idempotency keys for agent actions. The important compensation rule is simple: the forward key and undo key identify different intents, while every retry of a given undo reuses the same undo key.

Checkpoint recovery progress

Persist a checkpoint after each successful undo so a restarted worker resumes at the first unresolved compensation rather than replaying the entire sequence. A compensation item can carry states such as pending, running, succeeded, blocked, and awaiting manual resolution, together with attempt metadata and the last confirmed external result.

There is still a crash window between an external service completing an undo and the orchestrator saving succeeded. Idempotency closes that window: after restart, the worker resends the same compensation key and receives an equivalent result instead of repeating the effect. If the target operation is not idempotent, the worker must reconcile the current external state before it chooses whether to call again.

Checkpoint the workflow-level decision as well as individual commands. Persist whether compensation has started, which ordering constraints have been satisfied, and whether the pivot has been crossed. Without that state, two workers can disagree about whether to continue forward, begin compensation, or wait for a person.

Keep forward and compensating histories correlated but distinct. An operator needs to see that the original step succeeded, the later workflow failed, compensation attempted twice, and the final state remains blocked. Collapsing those events into a single failed status hides both the side effect and the outstanding obligation.

Escalation must carry enough evidence to act

Escalate after the defined retry budget is exhausted, authorization cannot be reacquired, a precondition shows concurrent change, the outcome remains ambiguous, or the domain requires a human decision. The escalation record should contain the workflow and step IDs, forward receipt, compensation command and key, relevant input digest, current external identifiers, authorization failure, last response, attempt history, and the exact unresolved obligation.

Preserve repeatedly failing compensation work in the same durable system used for dead-letter agent jobs, or in an equivalent recoverable queue. A dead-letter entry is not compensation completed. It is a checkpoint that prevents the failure from being lost and gives an operator or repair worker a controlled place to resume.

Manual actions need idempotency and checkpoints too. Record who claimed the case, what they observed, which approved action they attempted, and the resulting state. If an operator’s request times out, the next operator faces the same ambiguity as the automated worker; the stable compensation key and external receipt remain necessary.

An escalation path costs alerting, an operator view, access controls, and ownership outside the agent runtime. Omitting those costs does not remove them. It leaves unresolved side effects to be discovered through customer reports, billing discrepancies, or unrelated audits rather than through the workflow that created them.

A complete compensation record in practice

Consider an agent that creates an environment, grants a team access, and then publishes an instruction that cannot reliably be recalled. After environment creation, the durable record contains the returned environment ID, the authorized delete or decommission command, its fixed inputs, and the credential reference permitted to run it. Only then may the agent grant access.

After the grant succeeds, the next record contains the grant or binding ID and an authorized revoke command. The workflow can now compensate the grant and environment if a pre-publication validation fails. It should not merely restore a saved access list, because that could erase grants another administrator added meanwhile.

Publication is marked as the pivot. Before invoking it, the orchestrator persists the prepared state and a stable request key if the publication interface supports one. If its result is unknown, the workflow reconciles that result instead of guessing. After confirmed publication, remaining work is classified as retryable or manual; the pre-pivot delete and revoke actions are no longer a general escape route.

If compensation begins before publication and revocation succeeds but environment deletion times out, the revocation checkpoint remains succeeded. The deletion retries with its original compensation key. A restart resumes at deletion, not revocation. If deletion later fails because the environment now contains work owned by another actor, the conditional failure becomes a manual-resolution case rather than permission to erase the newer state.

That example contains no universal vendor behavior or timing assumption. Its value is the shape of the record: every completed side effect creates a durable recovery obligation, the pivot changes the permitted recovery direction, and both automatic and manual undo paths retain evidence of their progress.

Operational boundaries and cost

Compensation is appropriate when a multi-step operation commits separate side effects and later failure must leave a valid business state. It adds durable writes, idempotency storage, compensation handlers, credential lifecycle work, reconciliation queries, monitoring, and operator procedures. No supplied source provides a universal latency or engineering-cost figure, so those costs must be measured in the implementation rather than estimated here.

Do not attach a fictional undo to an irreversible effect. Mark the pivot instead. Do not compensate a read-only step, and do not use compensation to disguise a tool whose outcome cannot be observed. Where an action can be retried safely and forward progress remains valid, complete it through the retry policy; begin compensation only when the workflow’s recovery rules say forward progress has ended.

Test recovery at every boundary: before the external call, after the call but before its result is recorded, after the compensation starts, after the undo succeeds but before its checkpoint, at credential expiry, at retry exhaustion, and on both sides of the pivot. The expected result is not merely a stopped worker. It is a durable state from which automation or an authorized person can identify the next valid action.

What to check next

Before shipping a multi-step agent, confirm that every tool adapter reports stable resource identifiers, every completed side effect has a durable compensation record, compensation authority survives the initiating session, and each undo has a stable idempotency key. Confirm that the pivot is visible in both definition and run state, post-pivot work has bounded retries, checkpoints survive restarts, and an owned manual queue can finish obligations automation cannot.

Sources

  1. Microsoft Compensating Transaction patternlearn.microsoft.com
  2. Microsoft Saga patternlearn.microsoft.com
  3. AWS Builders' Library guidance on idempotent APIsaws.amazon.com

See also