Note: The result.source
and result.destination
from
BridgeResult
only
contain address and blockchain properties. To use provider methods for
recovery, you need to reconstruct full wallet contexts with adapter and chain
properties.
Bridge transfers can encounter two types of errors: hard errors that prevent execution and soft errors that allow remediation and retry. Bridge Kit provides comprehensive error handling to help you catch and recover from both scenarios.
Hard errors throw exceptions and include validation errors, configuration issues, and authentication problems. Soft errors occur during transfer execution but return recoverable transaction information, including insufficient balance, network timeouts, and RPC connectivity issues.
This guide helps you identify failure points, recover partial transfers, and implement error handling patterns.
This section explains how to identify where a transfer failed and resume the transfer manually.
Bridge transfers use Circle's CCTP protocol provider, which breaks each transaction into steps:
approve
- allows the contract to spend USDCburn
- burns USDC on source chain and generates an attestationfetchAttestation
- waits for Circle to sign the burn proofmint
- mints USDC on destination chain using the attestationWhen a transfer fails, Bridge Kit returns a BridgeResult
object showing which
steps completed and which failed. This lets you resume the transfer manually
using the CCTPv2BridgingProvider
, as shown in examples for
failed attestation fetch and
failed mint.
The key BridgeResult
properties for recovery are:
result.state
- shows whether the transfer succeeded or failed (pending
,
success
, error
)result.steps
- each object contains:
name
: the name of the stepstate
: the status of the steptxHash
: the transaction hash if the step completederror
: an error message if the step failedThis example shows a returned result
object for a transaction that failed when
fetching an attestation:
result.state: 'error'
result.steps: [
{ name: 'approve', state: 'success', txHash: '0x123...' },
{ name: 'burn', state: 'success', txHash: '0x456...' },
{ name: 'fetchAttestation', state: 'error', error: 'Network timeout' },
]
Note: The result.source
and result.destination
from
BridgeResult
only
contain address and blockchain properties. To use provider methods for
recovery, you need to reconstruct full wallet contexts with adapter and chain
properties.
This example shows how to check for successfully completed steps and use a helper function to find specific steps:
// Start a transfer that might fail
const result = await kit.bridge({
from: { adapter: sourceAdapter, chain: "Ethereum" },
to: { adapter: destAdapter, chain: "Arbitrum" },
amount: "100.0",
});
// Check which steps completed successfully
console.log("Transfer state:", result.state);
console.log("Steps:", result.steps);
// Helper function to find specific steps
const getStep = (stepName: string) =>
result.steps.find((step) => step.name === stepName);
const approveStep = getStep("approve");
const burnStep = getStep("burn");
const attestationStep = getStep("fetchAttestation");
const mintStep = getStep("mint");
This section describes how you can implement recovery patterns.
If a transfer fails, you can retry it using the retry
method. This method
takes the failed BridgeResult
and the to
and from
adapters.
This example shows how the retry method works:
const result = await kit.bridge({
from: { adapter, chain: "Ethereum" },
to: { adapter, chain: "Arbitrum" },
amount: "10",
});
if (result.state === "error") {
const retryResult = await kit.retry(result, {
from: adapter,
to: adapter,
});
console.log(inspect(retryResult, false, null, true));
} else {
console.log(inspect(result, false, null, true));
}
This example forces the mint step to fail and then retries with a valid to
adapter:
const adapter = createAdapterFromPrivateKey({
privateKey: process.env.PRIVATE_KEY as string,
});
// This is a fake adapter to force an error at the mint step
const fakeAdapter = createAdapterFromPrivateKey({
privateKey: ("0x" + "1".repeat(64)) as string,
});
const findErrorStep = (result: BridgeResult) => {
if (result.state === "error") {
return result.steps.find((step) => step.state === "error");
}
return null;
};
const kit = new BridgeKit();
const result = await kit.bridge({
from: { adapter, chain: "Ethereum_Sepolia" },
to: { adapter: fakeAdapter, chain: "Arbitrum_Sepolia" },
amount: "1",
});
console.log("INITIAL RESULT", inspect(result, false, null, true));
if (result.state === "error") {
const errorStep = findErrorStep(result);
if (
errorStep &&
errorStep.errorMessage?.includes("gas required exceeds allowance") // This is an example error message
) {
const retryResult = await kit.retry(result, {
from: adapter,
to: adapter, // To succeed this time we're using the correct adapter
});
console.log("RETRY RESULT", inspect(retryResult, false, null, true));
}
}
This section contains common issues and solutions to those issues.
Ensure you have sufficient USDC available in your wallet before transferring to avoid an insufficient balance error.
This example checks your wallet balance:
import "dotenv/config";
import { createAdapterFromPrivateKey as createViemAdapterFromPrivateKey } from "@circle-fin/adapter-viem-v2";
import { formatUnits } from "viem";
const adapter = createViemAdapterFromPrivateKey({
privateKey: process.env.PRIVATE_KEY as string,
});
const balanceAction = await adapter.prepareAction(
"usdc.balanceOf",
{},
{ chain: "Ethereum" },
);
const balance = await balanceAction.execute();
console.log(`USDC balance: ${formatUnits(BigInt(balance), 6)}`);
If a transaction is stuck or failed, check the transaction on the block explorer
with the returned txHash
. For Solana transfers, check Solana Explorer or
SolScan.
If the transaction failed mid-transfer, check the returned result.steps
object
to see which transaction steps completed
successfully.
Follow these practices for prevention, recovery, and monitoring to improve transfer reliability and recovery success rates.
Prevention
Recovery
Monitoring and debugging