Handling Failures and Recovery
Cross-chain swaps are non-atomic as they span two chains and a bridge provider in between. If the origin transaction succeeds but the bridge cannot complete, the user’s funds have left their wallet and need to be recovered.
This page explains what happens when a bridge fails, how recovery works, and what your integration should do for each scenario. For the happy-path status lifecycle, see Tracking Transaction Status.
Origin refund vs dest retry
The recovery path depends on where the transfer fails.
Origin refund. If the bridge fails, the bridge provider automatically returns the tokens on the origin chain. The status changes from refund_pending to refund_succeeded when the refund is complete.
Dest retry. If the bridge provider’s call on the destination chain fails, /status returns manualTransaction: null and 0x retries that call. A successful retry changes the status to bridge_filled.
CCIP is the exception. 0x does not yet submit dest retries for CCIP.
Arbitrum Bridge is the only bridge with a recovery deadline. 0x retries the recovery transaction a few times, but does not keep retrying until the deadline. After deadline, status can become failed.
How Failures Surface
When you poll GET /cross-chain/status and the response has status: "bridge_failed", it includes a failure object with everything needed to understand what went wrong and what to do next.
Do not stop polling on bridge_failed while failure.status is refund_pending or manual_action_required. When a destination retry succeeds, the top-level status changes to bridge_filled and the failure object is removed.
Failure Object Structure
Failure Reasons
Failure Statuses
Recovery Step Fields
What your integration should do
- Poll
/status. - If
failure.statusisrefund_pendingorrefund_succeeded, treat it as a refund. Do not ask the user to sign a transaction. - If
failure.statusismanual_action_required, continue polling. 0x submits the destination retry unless the bridge is CCIP. Do not ask the user to sign a transaction. - If
failure.statusisfailed, contact 0x support with thezidfrom/status.
Refund Token and Chain
The refund token and chain depend on where the failure happened and what the route looked like. The refund is not always in the user’s original sellToken on originChain. What you get back depends on the bridge (see table below) and the route composition.
The main thing to understand: the refund token is based on the bridge step’s sell or buy token and not the route’s sellToken. If the route included a swap or wrap before the bridge, the bridge step’s tokens differ from what the user started with:
The recovery.token, recovery.chainId, and recovery.amount fields contain the exact details. For destination retries, these fields describe delivery on the destination chain, not an origin refund.
Tracking Refund Progress
For automatic refunds, keep polling /status:
bridge_failed+refund_pending- refund initiatedbridge_failed+refund_succeeded- refund confirmed,failure.transactionshas refund tx hash andrecovery.settledAmounthas actual refunded amount
Tracking Dest Retry Progress
For destination retries submitted by 0x, continue polling /status:
- When the top-level status is
bridge_failedandfailure.statusismanual_action_required, the destination retry is in progress.manualTransactionis null. Retry transaction hashes may appear infailure.transactionsand the top-leveltransactionsarray before they are mined. A transaction hash does not mean that recovery is complete. - When the retry succeeds, the top-level status changes to
bridge_filledand thefailureobject is removed. - If
failure.statusremainsmanual_action_required, the destination retry has not completed. Only Arbitrum Bridge can move tofailedafterdeadline.
Recovery Behavior by Bridge
In this table, “bridge step’s sell/buy token” refers to the tokens on the bridge step specifically - not the user’s original sellToken/buyToken from the quote request. If the route included a swap or wrap before the bridge, these are different. See Refund Token and Chain above.
For bridges marked “Not tracked”, the 0x status service does not track the recovery process. The bridge provider may still offer recovery through their own tools.
Example: Automatic Refund (Across)
Example: Dest Retry in Progress (Stargate)
A Stargate transfer to Arbitrum (chain 42161) ran out of gas while executing on the destination chain. 0x submits a recovery transaction to retry the destination call, so manualTransaction is null. Recovery transaction hashes can appear in transactions while status remains bridge_failed. Keep polling until the status changes to bridge_filled.
Example: Arbitrum Bridge Deadline
0x retries the recovery transaction a few times, but does not keep retrying until deadline. The user does not need to sign or submit the recovery transaction.