Fig. 07 · Forward legs, a failure at the partner, and compensation in reverse order with an on-chain refund.
The leg contract
idempotencyKey of ${sagaId}:${stepIndex}. Bookings without an external reference are keyed on it, so re-running a step never double-books. Legs that move money first look for the movement by its Corridor Reference, so a retry after a crash between “sent” and “recorded” finds the transfer instead of sending it again.
State
Failure semantics
Retryable errors
Retryable errors
The step is retried up to
maxAttempts with backoff. Network errors and RPC timeouts land here.NonRetryableError
NonRetryableError
The step fails at once (a validation that a retry cannot change), and the saga compensates.
Compensation, in reverse order
Compensation, in reverse order
Completed steps are undone from last to first, each through its own
compensate with its own bookings. Order matters: unwinding out of order can leave a balanced ledger with a false audit trail. A forced partner failure on testnet refunds the Tempo leg on-chain and reverses every booking.ManualReviewError
ManualReviewError
No retries and no automatic compensation. Used when money has already left Corridor’s control, for example a partner rejecting a payout it was already funded for. Undoing earlier legs would be wrong until the partner’s refund is confirmed, so a human closes it.
Leases
A saga is driven by one worker at a time.run() takes a lease (lease_owner, lease_until, default 120 s) with a conditional update; another live worker that tries gets the current status back without doing work. If a worker dies, its lease expires and any worker can resume from the last durable step.
Fault injection
SagaInput.faults.failAt makes a chosen leg kind fail, which is how the demos prove compensation on-chain. The console button “Payout with partner failure” uses it; see Failure and compensation.