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. Catch the exported WDK error classes and compare reason constants where an error exposes them; do not match message text:
import {
MaximumFeeExceededError,
TransactionError,
TransactionErrorReason,
ValueError
} from '@tetherto/wdk-wallet'
try {
const result = await account.sendTransaction({
to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh',
value: 100000n
})
console.log('Transaction hash:', result.hash)
} catch (error) {
if (error instanceof TransactionError &&
error.reason === TransactionErrorReason.INSUFFICIENT_BALANCE) {
console.error('Not enough funds in wallet')
} else if (error instanceof MaximumFeeExceededError) {
console.error('Transaction fee exceeds transactionMaxFee')
} else if (error instanceof ValueError) {
console.error('Transaction amount or input selection is invalid')
} else {
throw error
}
}Connection Errors
Network and backend failures use ProviderError. Inspect error.reason instead of parsing a provider message. For BlockbookClient, HTTP 401 maps to UNAUTHORIZED, 403 to FORBIDDEN, 408 or 504 to REQUEST_TIMEOUT, other 5xx responses to INTERNAL_SERVER_ERROR, and other non-2xx responses to NETWORK_ERROR. WebSocket connection failures use NETWORK_ERROR; WebSocket server-response and fee-estimation failures use INTERNAL_SERVER_ERROR.
import { ProviderError, ProviderErrorReason } from '@tetherto/wdk-wallet'
try {
const balance = await account.getBalance()
console.log('Balance:', balance, 'satoshis')
} catch (error) {
if (error instanceof ProviderError &&
(error.reason === ProviderErrorReason.NETWORK_ERROR ||
error.reason === ProviderErrorReason.REQUEST_TIMEOUT)) {
console.error('Provider unavailable or timed out; retry according to your policy')
} else if (error instanceof ProviderError &&
(error.reason === ProviderErrorReason.UNAUTHORIZED ||
error.reason === ProviderErrorReason.FORBIDDEN)) {
console.error('Check provider credentials and permissions')
} else if (error instanceof ProviderError &&
error.reason === ProviderErrorReason.INTERNAL_SERVER_ERROR) {
console.error('Provider failed internally; retry or use another client')
} else {
throw error
}
}Invalid mnemonic and unsupported bip values throw ValueError while an account is created. Bitcoin accounts do not support token operations: getTokenBalance(tokenAddress), quoteTransfer(options), and transfer(options) throw UnsupportedOperationError.
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:
import { NoSuchElementError, ValueError } from '@tetherto/wdk-wallet'
try {
const receipt = await account.getTransaction(transactionHash)
console.log('Current finality:', receipt.finality)
} catch (error) {
if (error instanceof ValueError) {
console.error('Expected a 64-character hexadecimal transaction ID')
} else if (error instanceof 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:
import { TimeoutError } from '@tetherto/wdk-wallet'
try {
const receipt = await account.waitForTransaction(transactionHash, {
target: 'final'
})
console.log('Final transaction:', receipt.hash)
} catch (error) {
if (error instanceof 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.