Why Does My Cardano Transaction Keep Failing?
Transaction failures on Cardano often stem from UTXO conflicts, insufficient inputs, or incorrect fee calculations. Learn the root causes and how Mesh SDK prevents these issues automatically.
Cardano transaction failures typically occur due to UTXO conflicts, insufficient inputs for the desired outputs, incorrect fee calculations, or exceeding transaction size limits. Mesh SDK eliminates most failures by automatically handling UTXO selection, fee estimation, and change calculation. Rather than manually managing these low-level details, you describe the transaction intent and let Mesh construct a valid transaction.
Why This Happens
Cardano's UTXO model fundamentally differs from account-based blockchains like Ethereum. Instead of a single balance that updates in place, Cardano wallets contain discrete "coins" called UTXOs (Unspent Transaction Outputs). Every transaction consumes existing UTXOs and creates new ones. This architecture provides stronger security guarantees and enables deterministic transaction validation, but it also creates failure modes that don't exist on account-based chains.
Concurrent Transaction Conflicts
When you build a transaction, you select specific UTXOs as inputs. If another transaction consumes one of those UTXOs before yours confirms, your transaction becomes invalid. This happens frequently in dApps where users might click a button multiple times or where backend systems process multiple requests simultaneously.
In account-based systems, transactions simply execute in sequence with the final state reflecting all operations. In Cardano, you must explicitly handle the case where your selected inputs no longer exist. This is not a bug—it's a fundamental property of how UTXOs work.
Insufficient Input Selection
A common frustration occurs when your wallet shows sufficient ADA balance, but transactions fail with "insufficient funds" errors. This happens because:
- Your ADA is locked in UTXOs alongside native tokens that you're not spending
- The minimum UTXO value requirement means some outputs cannot be created
- Transaction fees reduce your available balance below what's needed
- UTXOs are fragmented into many small amounts that create oversized transactions when combined
Fee Calculation Complexity
Transaction fees on Cardano depend on transaction size in bytes, which depends on:
- Number of inputs (each adds roughly 100-150 bytes)
- Number of outputs (varies based on address type and tokens)
- Witness data (signatures add significant size)
- Metadata and auxiliary data
- Script execution units for smart contract transactions
You cannot know the exact fee until you build the complete transaction, but you cannot build the transaction without knowing fees for change calculation. This circular dependency requires iterative solving that's error-prone when done manually.
Transaction Size Limits
Cardano enforces a maximum transaction size (currently 16KB on mainnet). Transactions with many inputs, many outputs, large metadata, or complex script witnesses can exceed this limit. This often surprises developers who successfully test with small transactions but fail when real-world conditions require larger operations.
How Mesh Solves This
Mesh SDK provides the MeshTxBuilder class that handles all these complexities automatically. You describe what you want to accomplish, and Mesh figures out how to construct a valid transaction.
import { MeshTxBuilder, BlockfrostProvider, BrowserWallet } from "@meshsdk/core";
const provider = new BlockfrostProvider("<your-api-key>");
const wallet = await BrowserWallet.enable("eternl");
const txBuilder = new MeshTxBuilder({
fetcher: provider,
submitter: provider,
});
const unsignedTx = await txBuilder
.txOut(recipientAddress, [{ unit: "lovelace", quantity: "5000000" }])
.changeAddress(await wallet.getChangeAddress())
.selectUtxosFrom(await wallet.getUtxos())
.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);Automatic UTXO Selection
The selectUtxosFrom method implements intelligent coin selection algorithms that:
- Choose UTXOs that minimize transaction size
- Avoid UTXOs with tokens you don't intend to spend when possible
- Handle the minimum UTXO value requirement automatically
- Select additional inputs when initial selection proves insufficient
Iterative Fee Calculation
When you call complete(), Mesh:
- Builds an initial transaction estimate
- Calculates the required fee
- Adjusts change outputs accordingly
- Rebuilds if the adjustment changed transaction size
- Repeats until convergence
This typically takes 2-3 iterations and happens in milliseconds.
Smart Change Handling
Mesh calculates change outputs that:
- Meet minimum UTXO value requirements
- Include any tokens from inputs that aren't being sent elsewhere
- Minimize the number of change outputs to reduce future fragmentation
Error Prevention
Rather than failing cryptically, Mesh provides clear error messages when:
- Selected UTXOs cannot satisfy the requested outputs
- The transaction would exceed size limits
- Required tokens are unavailable
Quick Start
Follow these steps to eliminate common transaction failures:
Step 1: Install Mesh SDK
npm install @meshsdk/core @meshsdk/reactStep 2: Configure your provider and wallet
import { MeshTxBuilder, BlockfrostProvider, BrowserWallet } from "@meshsdk/core";
const provider = new BlockfrostProvider("<your-blockfrost-api-key>");
const wallet = await BrowserWallet.enable("eternl");Step 3: Build transactions using MeshTxBuilder
const txBuilder = new MeshTxBuilder({
fetcher: provider,
submitter: provider,
});
// Always fetch fresh UTXOs immediately before building
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const unsignedTx = await txBuilder
.txOut(recipientAddress, [{ unit: "lovelace", quantity: "10000000" }])
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);Handling Remaining Failures
Even with Mesh, some failures require application-level handling:
Retry Logic
Network issues or mempool congestion can cause transient failures. Implement retry logic with exponential backoff:
async function submitWithRetry(wallet, signedTx, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await wallet.submitTx(signedTx);
} catch (error) {
if (attempt === maxRetries - 1) throw error;
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
}
}
}Fresh State Fetching
Always fetch UTXOs immediately before building transactions, not at page load:
// Wrong: stale UTXOs from earlier
const cachedUtxos = await wallet.getUtxos();
// ... time passes, user clicks button ...
const tx = await txBuilder.selectUtxosFrom(cachedUtxos).complete(); // May fail
// Correct: fresh UTXOs at transaction time
async function buildTransaction() {
const freshUtxos = await wallet.getUtxos();
return await txBuilder.selectUtxosFrom(freshUtxos).complete();
}User Feedback
Inform users when transactions are pending and prevent double-submission:
const [isSubmitting, setIsSubmitting] = useState(false);
async function handleSubmit() {
if (isSubmitting) return;
setIsSubmitting(true);
try {
const txHash = await submitTransaction();
// Show success
} catch (error) {
// Show error with actionable message
} finally {
setIsSubmitting(false);
}
}Advanced Scenarios
For complex use cases, Mesh provides additional control:
Multi-Asset Transactions
When sending native tokens, Mesh ensures the accompanying ADA meets minimum requirements:
const tx = await txBuilder
.txOut(recipient, [
{ unit: "lovelace", quantity: "2000000" },
{ unit: policyId + assetName, quantity: "1" }
])
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();Script Transactions
For smart contract interactions, Mesh handles execution budgets and collateral:
const tx = await txBuilder
.spendingPlutusScript(languageVersion)
.txIn(scriptUtxo.input.txHash, scriptUtxo.input.outputIndex)
.txInScript(scriptCbor)
.txInDatumValue(datum)
.txInRedeemerValue(redeemer)
.txOut(outputAddress, outputValue)
.txInCollateral(collateralUtxo.input.txHash, collateralUtxo.input.outputIndex)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos)
.complete();Related Challenges
Understanding transaction failures connects to other Cardano development challenges:
- Understanding the UTXO Model explains the fundamental architecture causing many transaction issues
- Coin Selection Problems dives deeper into input selection strategies
- Wallet Integration Issues covers ensuring reliable wallet connections
Next Steps
With transaction failures under control, you can build more reliable Cardano applications. Explore the Challenges Hub for solutions to other common obstacles, or visit the full documentation to learn about advanced Mesh features.
Common Cardano Development Challenges
Discover the most frequent obstacles Cardano developers face when building dApps, from transaction failures to UTXO management, and learn how Mesh SDK provides elegant solutions to overcome each challenge.
Understanding Cardano's UTXO Model
Cardano's UTXO model differs fundamentally from Ethereum's account model. Learn how UTXOs work, why they cause confusion for developers, and how Mesh SDK abstracts this complexity.