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:
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():
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.
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:
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:
const feeRates = await wallet.getFeeRates()
console.log('Normal fee rate:', feeRates.normal)
console.log('Fast fee rate:', feeRates.fast)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:
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.