WDK logoWDK documentation

Handle Errors

Handle Spark transaction and connection failures, plus fees and disposal.

This guide explains how to handle transaction errors, handle connection errors, handle unsupported operations and missing transfers, and apply best practices for fees and secure cleanup.

Transaction Errors

Operations such as account.sendTransaction(), account.transfer(), account.payLightningInvoice(), and account.withdraw() can throw. Wrap each call in try/catch and use public WDK error classes (and ProviderError.reason for SparkScan failures) instead of matching message text:

Handle Transaction Errors
try {
  const result = await account.sendTransaction({
    to: 'spark1...',
    value: 1000000
  })
  console.log('Transaction hash:', result.hash)
} catch (error) {
  console.error('Send failed:', error.message)
}

You can isolate Lightning failures by wrapping account.payLightningInvoice():

Handle Lightning Errors
try {
  const payment = await account.payLightningInvoice({
    encodedInvoice: 'lnbc500u1p...',
    maxFeeSats: 1000
  })
  console.log('Payment id:', payment.id)
} catch (error) {
  console.error('Lightning payment failed:', error.message)
}

Connection Errors

When SparkScan returns a non-success HTTP response, it throws ProviderError. Inspect error.reason instead of matching response messages. SparkScan maps HTTP 401 to UNAUTHORIZED, 403 to FORBIDDEN, 408 and 504 to REQUEST_TIMEOUT, other 5xx responses to INTERNAL_SERVER_ERROR, and other non-2xx responses to NETWORK_ERROR.

Handle SparkScan Provider Errors
import { ProviderError, ProviderErrorReason } from '@tetherto/wdk-wallet'

try {
  const balance = await account.getBalance()
  console.log('Balance:', balance, 'satoshis')
} catch (error) {
  if (!(error instanceof ProviderError)) {
    throw error
  }

  switch (error.reason) {
    case ProviderErrorReason.REQUEST_TIMEOUT:
      console.error('SparkScan request timed out')
      break
    case ProviderErrorReason.UNAUTHORIZED:
    case ProviderErrorReason.FORBIDDEN:
      console.error('Check SparkScan credentials and endpoint access')
      break
    case ProviderErrorReason.INTERNAL_SERVER_ERROR:
    case ProviderErrorReason.NETWORK_ERROR:
      console.error('Check SparkScan availability and connectivity')
      break
    default:
      throw error
  }
}

Unsupported Operations and Missing Transfers

Spark cannot produce standalone signed transaction payloads, so account.signTransaction() always throws UnsupportedOperationError. Spark accounts are addressed by index, so wallet.getAccountByPath() throws the same error. account.getTransaction() throws NoSuchElementError when Spark returns no transfer for the requested ID. Use instanceof checks and rethrow unknown errors:

Handle Unsupported Operations and Missing Transfers
import { NoSuchElementError, UnsupportedOperationError } from '@tetherto/wdk-wallet'

try {
  await account.signTransaction({
    to: 'spark1...',
    value: 1000000
  })
} catch (error) {
  if (error instanceof UnsupportedOperationError) {
    console.error('Use account.sendTransaction() for Spark transfers')
  } else {
    throw error
  }
}

try {
  const transaction = await account.getTransaction('transfer-id')
  console.log('Transfer status:', transaction.finality)
} catch (error) {
  if (error instanceof NoSuchElementError) {
    console.error('No Spark transfer was found for this ID')
  } else {
    throw error
  }
}

Best Practices

Fee management

Native Spark sends and token transfers report zero fees, but withdrawals and Lightning payments can charge fees. Use wallet.getFeeRates() for wallet-level rate placeholders and account.quotePayLightningInvoice() for Lightning sends:

Inspect Spark Fee Rates
const feeRates = await wallet.getFeeRates()
console.log('Normal fee rate:', feeRates.normal)
console.log('Fast fee rate:', feeRates.fast)
Quote Lightning Fee Before Paying
const lightningFee = await account.quotePayLightningInvoice({
  encodedInvoice: 'lnbc500u1p...'
})
console.log('Estimated Lightning fee:', Number(lightningFee), 'satoshis')

quoteWithdraw() should run before withdraw() so you understand cooperative exit costs.

Dispose of sensitive data

Clear keys from memory when a session ends. Call account.dispose() for each account and wallet.dispose() on the manager:

Dispose Wallet Resources
try {
  const result = await account.sendTransaction({
    to: 'spark1...',
    value: 1000000
  })
  console.log('Transaction hash:', result.hash)
} finally {
  account.dispose()
  wallet.dispose()
}

After dispose(), the account cannot sign new operations. Call disposal when the wallet UI or job is finished.

Next Steps

Return to the Spark wallet usage overview or open the API Reference for full method signatures.

On this page