WDK logoWDK documentation
BitcoinGuides

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:

Handle Transaction Errors
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:

Handle Connection Errors
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:

Handle a Transaction 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:

Handle a Finality Timeout
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():

Get Fee Rates
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:

Dispose Resources
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.

On this page