Skip to main content

Error Handling

Evolution SDK is built on Effect, providing structured error handling with typed errors. Every operation that can fail returns an Effect<Success, Error> type, making error cases explicit and composable.

Error Types

ErrorWhen
TransactionBuilderErrorBuild phase failures (insufficient funds, invalid parameters)
ProviderErrorProvider communication issues (network, API errors)
EvaluationErrorPlutus script evaluation failures

Build Modes

Evolution SDK offers three ways to handle errors from .build():

The standard .build() returns a Promise. Errors throw as exceptions:

import { Address, Assets, preprod, Client } from "@evolution-sdk/evolution"

const client = Client.make(preprod)
.withBlockfrost({
baseUrl: "https://cardano-preprod.blockfrost.io/api/v0",
projectId: process.env.BLOCKFROST_API_KEY!
})
.withSeed({ mnemonic: process.env.WALLET_MNEMONIC!, accountIndex: 0 })

try {
const tx = await client
.newTx()
.payToAddress({
address: Address.fromBech32("addr_test1vrm9x2dgvdau8vckj4duc89m638t8djmluqw5pdrFollw8qd9k63"),
assets: Assets.fromLovelace(2_000_000n)
})
.build()

const signed = await tx.sign()
await signed.submit()
} catch (error) {
console.error("Transaction failed:", error)
}

All Error Types

Transaction Building

ErrorSourceCommon Causes
TransactionBuilderErrorClient.newTx().build()Insufficient funds, invalid parameters, missing required fields
EvaluationErrorScript evaluation phasePlutus validator rejected the transaction, exceeded execution limits
CoinSelectionErrorUTxO selection phaseWallet has insufficient funds, no UTxOs match required assets

Provider

ErrorSourceCommon Causes
ProviderErrorAny provider callNetwork timeout, invalid API key, rate limiting, endpoint down

Encoding / Decoding

ErrorSourceCommon Causes
DataErrorData.withSchema() codec operationsSchema mismatch, invalid PlutusData structure
CBORErrorCBOR encoding/decodingMalformed CBOR bytes, unexpected data format
UPLCErrorUPLC operationsInvalid flat encoding, corrupted script bytes

Wallet / Key

ErrorSourceCommon Causes
WalletErrorWallet operationsCIP-30 wallet not connected, user rejected, missing API method
DerivationErrorKey derivationInvalid mnemonic, bad derivation path
PrivateKeyErrorPrivate key operationsInvalid key bytes, signing failure
Bip32PrivateKeyErrorBIP-32 HD keysInvalid extended key
Bip32PublicKeyErrorBIP-32 public keysInvalid public key derivation

Script

ErrorSourceCommon Causes
NativeScriptErrorNative script operationsInvalid timelock, bad multi-sig configuration

Debugging Common Errors

"Insufficient funds" (CoinSelectionError)

try {
const tx = await client.newTx()
.payToAddress({ address, assets: Assets.fromLovelace(1000_000_000n) })
.build()
} catch (e) {
// Check: Does the wallet have enough ADA?
// Check: Are UTxOs locked at script addresses?
// Check: Is there enough ADA for fees + min UTxO?
console.error(e)
}

Fix: Query your wallet UTxOs first to verify available balance.

"Script evaluation failed" (EvaluationError)

try {
const tx = await client.newTx()
.collectFrom({ inputs: scriptUtxos, redeemer: Data.constr(0n, []) })
.attachScript({ script: validatorScript })
.build()
} catch (e) {
// Check: Is the redeemer correct for your validator?
// Check: Does the datum match what the validator expects?
// Check: Are required signers included?
console.error(e)
}

Fix: Use debug labels (label: "my-operation") on collectFrom to identify which script failed.

"Provider request failed" (ProviderError)

try {
const tx = await client.newTx()
.payToAddress({ address, assets })
.build()
} catch (e) {
// Check: Is your API key valid?
// Check: Is the provider endpoint reachable?
// Check: Are you on the correct network (preprod vs mainnet)?
console.error(e)
}

Fix: Verify your provider configuration, API key, and network connectivity.

"CBOR decoding failed" (CBORError)

import { CBOR } from "@evolution-sdk/evolution"

try {
const value = CBOR.fromCBORHex("invalid-hex")
} catch (e) {
console.error(e)
}

Fix: Verify the hex string is valid and the correct encoding level. Use UPLC.getCborEncodingLevel() to check script encoding.

Inspecting Errors

All Evolution SDK errors are tagged errors with structured fields. You can inspect them by checking the _tag property.

Identifying Error Types

try {
const tx = await client.newTx()
.payToAddress({ address, assets })
.build()
const signed = await tx.sign()
await signed.submit()
} catch (e: any) {
switch (e._tag) {
case "TransactionBuilderError":
console.error("Build failed:", e.message)
break
case "EvaluationError":
console.error("Script failed:", e.message)
if (e.failures) {
for (const f of e.failures) {
console.error(` [${f.purpose}] ${f.label ?? "unlabeled"}: ${f.validationError}`)
if (f.traces.length > 0) {
console.error(" Traces:", f.traces.join(", "))
}
}
}
break
case "CoinSelectionError":
console.error("Insufficient funds:", e.message)
break
case "ProviderError":
console.error("Provider issue:", e.message)
break
default:
console.error("Unknown error:", e)
}
}

EvaluationError Script Failures

When a Plutus script fails, EvaluationError contains a failures array with detailed information about each failed script:

FieldTypeDescription
purposestring"spend", "mint", "withdraw", or "publish"
indexnumberIndex within the purpose category
labelstring?Your debug label from collectFrom({ label: "..." })
validationErrorstringThe error message from the validator
tracesstring[]Execution traces emitted by the script
scriptHashstring?Hash of the failed script
utxoRefstring?UTxO reference (for spend redeemers)
policyIdstring?Policy ID (for mint redeemers)
credentialstring?Credential hash (for withdraw/cert redeemers)

Using Labels for Debugging

const tx = await client
.newTx()
.collectFrom({
inputs: escrowUtxos,
redeemer: Data.constr(0n, []),
label: "claim-escrow"
})
.collectFrom({
inputs: vestingUtxos,
redeemer: Data.constr(1n, []),
label: "unlock-vesting"
})
.attachScript({ script: escrowScript })
.attachScript({ script: vestingScript })
.build()

When a script fails, the error tells you exactly which operation caused it:

EvaluationError: Script evaluation failed
[spend] claim-escrow: Validator returned False
Traces: "deadline not reached", "current time: 1735600000"

Safe Parsing with Either

const result = await client
.newTx()
.payToAddress({ address, assets })
.buildEither()

if (result._tag === "Left") {
console.error("Failed:", result.left)
} else {
const signed = await result.right.sign()
await signed.submit()
}

Next Steps