Skip to main content
Sometimes transactions fail. This guide outlines common failure reasons and provides tools to help you identify the underlying causes.
Reverted transactions are marked as “failed” and will not be executed. There is no auto-retry mechanism for failed transactions; you must manually resubmit them if needed.

Common Error Types

Transaction errors typically fall into two categories: validation failures and execution failures.

Validation Errors

Validation errors occur when the provided transaction data is invalid. These errors commonly relate to invalid data (e.g., signature) during transaction approval. Common issues include:
  • Malformed signatures
  • Invalid signatures (e.g., from an unauthorized wallet)
  • Insufficient signer permissions for transaction approval
An example of a validation error:
Troubleshooting steps:
  • Verify the transaction signer is valid
  • Confirm you are signing the correct message data
  • Verify the signing private key matches the signer wallet

Execution Errors

Execution errors occur when a transaction is reverted during runtime. These typically relate to the calls provided during transaction creation. Common causes include:
  • Another transaction with the same call was executed already
  • State changes between transaction creation and execution (e.g. asset price updated exceeding slippage allowance)
The Crossmint API typically provides specific revert reasons:
On EVM smart wallets, revert can also include a debug object that classifies the failure and points to the call that reverted. See Revert Debug Object. Troubleshooting:
  • Check revert.debug.details.code when present, and follow the matching action in Failure Codes
  • Check for similar previous transactions
  • Attempt to resubmit with the same parameters

Wallet Errors

WalletNotAvailableError

Thrown by getWallet() when no wallet exists for the given parameters. This is expected in the get-then-create pattern where you first attempt to retrieve a wallet and fall back to creating one.
In previous SDK versions, getOrCreateWallet() handled this automatically. In V1, wallet retrieval and creation are separate operations. See the migration guide for details.

Common Pitfalls

These are frequent sources of confusion when working with the V1 SDK.

Read-only wallet from getWallet()

getWallet() returns a wallet with no active signer unless a device signer is found locally. Calling send(), approve(), or addSigner() on a read-only wallet throws an error. Fix: Call wallet.useSigner() to activate a signer before performing signing operations, or rely on automatic device signer initialization on the client side.

useSigner() does not accept locator strings

In V1, useSigner() only accepts signer config objects. Passing a locator string (e.g., "evm-keypair:0x...") results in a type error.

getWallet() parameter mismatch between client and server

getWallet() has different signatures on client and server. Using the wrong one throws an error.
  • Client-side: getWallet({ chain: "base-sepolia" }) — pass wallet args directly. Passing a walletLocator string as the first argument throws.
  • Server-side: getWallet("0xWalletAddress", { chain: "base-sepolia" }) — requires a walletLocator string as the first parameter. Omitting it throws: "getWallet on server side requires a walletLocator parameter."

Client-side input validation errors

The V1 SDK validates inputs before making API calls, failing fast with descriptive errors:

Other Errors

If you encounter transaction failures without error details or receive no error message, there may be an issue with the Crossmint infrastructure. First, check the status page to verify API availability and try again later. If the problem continues, contact Crossmint support.

Signer Is Not Active After Adding

When you add a signer on EVM, the operation is either a transaction or a signature, depending on deployImmediately:
  • TypeScript SDK (@crossmint/wallets-sdk, and React / React Native): addSigner() defaults to deployImmediately: true, which installs the signer onchain as soon as the registration transaction confirms.
  • REST API: POST /2025-06-09/wallets/{walletLocator}/signers defaults to deployImmediately: false for EVM, so the signer is installed lazily on its first transaction after the signature is approved.
  • Flutter, Kotlin, and Swift SDKs: These SDKs do not currently expose deployImmediately, so they use the REST API default (deferred signature flow on EVM).
If a signer is stuck in pending or awaiting-approval, check whether the response contains a transactionId or a signatureId and approve the correct object: See Installation Timing for details.

Debugging

The Crossmint API simulates all transactions before submitting them onchain. Failed simulations prevent onchain execution. In most cases, the transaction fails during the simulation. If that is the case, the revert.reason and revert.reasonData fields describe the failure, and on EVM smart wallets revert.debug adds structured details. Otherwise, you can use a block explorer to inspect the transaction.

Revert Debug Object

revert.debug is an optional object on the revert of failed EVM smart wallet transactions. It appears in transaction responses and in transaction webhook payloads. revert.reason and revert.reasonData keep their existing format, so code that reads them continues to work. revert.debug is only available for EVM smart wallets and is absent when the chain returned no information about the failure, so always fall back to revert.reason. The classification is best effort: details.code is unknown when the cause cannot be determined, and call is omitted when the failing call cannot be identified. revert.debug contains:
  • message: a one-sentence summary of the failure
  • retryable: true when resubmitting the same transaction can succeed, either after you fix the cause (for example funds or gas) or without changes (for example a nonce conflict, a gas sponsorship failure, or a failure with no revert data). false when a contract call itself reverts and the calls must change.
  • chain and blockNumber: the chain and the block whose state the failure was observed against
  • revert: the raw revert returned by the chain — selector, name, signature, and positional args. selector is absent when the revert carried no data; name and signature are absent when the error could not be decoded.
  • call: the call in the batch that reverted — index, to, value, function, and args. Absent when the failure cannot be attributed to a call, for example during wallet validation.
  • details: the classified root cause. Branch on details.code; the other fields depend on the code.
Each entry in args has a Solidity type and a value, with integers as decimal strings. args[].name is present only when the ABI was supplied in the transaction request. To reproduce the failure, send an eth_call from the EntryPoint to the wallet with onChain.userOperation.callData at blockNumber. Hook-enabled operations prefix the calldata with the executeUserOp selector, which must be stripped, and wallets not yet deployed need a state override (proxy code plus the EIP-1967 implementation slot). This example shows a USDC transfer that exceeds the wallet balance:

Failure Codes

required and available are decimal strings in the token’s smallest unit. Crossmint returns an insufficient_* or out_of_gas code only when it can confirm the cause. Otherwise the same revert is reported as contract_revert, with the decoded error in revert, so handle every code in the table.

EntryPoint Codes

When details.code is account_abstraction, entryPointCode holds the EntryPoint code and entryPointMessage holds the EntryPoint’s own message.

Block Explorer

If the transaction was included onchain, you can check its status using a block explorer.