Handle Errors
Handle errors, manage fees, and dispose of sensitive data.
This guide explains how to handle transaction errors, handle connection errors, and follow best practices for fee management and memory cleanup.
Transaction Errors
Transactions sent via account.sendTransaction() can fail for several reasons. Wrap transaction calls in a try/catch block to handle specific error types:
try {
const result = await account.sendTransaction({
to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh',
value: 100000n
})
console.log('Transaction hash:', result.hash)
} catch (error) {
if (error.message.includes('Insufficient balance')) {
console.error('Not enough funds in wallet')
} else if (error.message.includes('Exceeded maximum fee')) {
console.error('Transaction fee exceeds transactionMaxFee')
} else if (error.message.includes('dust limit')) {
console.error('Amount is below the minimum dust limit')
} else if (error.message.includes('Invalid address')) {
console.error('Recipient address is invalid')
} else {
console.error('Transaction failed:', error.message)
}
}Connection Errors
Network issues with the Electrum server can cause failures across all operations. Handle connection errors at a higher level:
try {
const balance = await account.getBalance()
console.log('Balance:', balance, 'satoshis')
} catch (error) {
if (error.message.includes('ECONNREFUSED') || error.message.includes('timeout')) {
console.error('Network error: check Electrum server connection')
} else if (error.message.includes('Invalid seed')) {
console.error('Invalid seed phrase provided')
} else {
console.error('Operation failed:', error.message)
}
}Transaction Status Errors
getTransaction() validates the transaction ID and searches this account address's history. Handle malformed and unknown transactions on that one-shot lookup:
try {
const receipt = await account.getTransaction(transactionHash)
console.log('Current finality:', receipt.finality)
} catch (error) {
if (error.name === 'ValueError') {
console.error('Expected a 64-character hexadecimal transaction ID')
} else if (error.name === 'NoSuchElementError') {
console.error('Transaction is not in this account address history')
} else {
throw error
}
}Use waitForTransaction() when the hash may still be propagating. It consumes NoSuchElementError as a transient polling state, so the caller normally handles a timeout instead:
try {
const receipt = await account.waitForTransaction(transactionHash, {
target: 'final'
})
console.log('Final transaction:', receipt.hash)
} catch (error) {
if (error.name === 'TimeoutError') {
console.error('Transaction did not reach the requested finality in time')
} else {
throw error
}
}Polling also tolerates, by default, up to three consecutive provider errors. Bitcoin does not expose reliable dropped detection through this history flow, so a never-seen or evicted transaction ends in TimeoutError.
Best Practices
Fee Management
You can retrieve current network fee rates using wallet.getFeeRates():
const feeRates = await wallet.getFeeRates()
console.log('Normal fee rate:', feeRates.normal, 'sat/vB')
console.log('Fast fee rate:', feeRates.fast, 'sat/vB')Set transactionMaxFee to stop sendTransaction() and signTransaction() when the estimated BTC network fee exceeds your limit.
wallet.getFeeRates() fetches rates from the mempool.space API, while account.sendTransaction() estimates fees from the connected Electrum server. Use getFeeRates() for display purposes.
Dispose of Sensitive Data
For security, clear sensitive data from memory when a session is complete. Use account.dispose() and wallet.dispose() to securely wipe private keys:
try {
const result = await account.sendTransaction({
to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh',
value: 100000n
})
console.log('Transaction hash:', result.hash)
} finally {
account.dispose()
wallet.dispose()
}Always call dispose() when finished with accounts. Private keys are securely wiped from memory using sodium_memzero. Electrum connections are automatically closed. Disposal is irreversible.