Appearance
Errors
How Coinbase failures surface in React and what to do at each step.
How errors surface
- Hook
errorstate —useCoinbaseConnection().error,useCoinbaseWithdrawal().error, balances/networks hooks, etc. - Thrown / rejected promises — the same actions also reject (hooks rethrow after setting
error). - Events — e.g.
useOnCoinbaseSessionExpired;useOnOAuthError/useOnErrorfor popup failures.
Amount validation errors
useCoinbaseAmount().error / .warning are CoinbaseAmountError ({ crypto, fiat }), not strings. Each half is an AmountValidationError ({ code, message, metadata }); fiat is null when no exchange rate. Switch on error.crypto.code for i18n; use error.crypto.message / error.fiat?.message as English fallbacks.
Codes: invalid_amount, amount_zero, below_min, above_balance (hard: above raw balance), above_spendable (soft: above balance minus fee reserve). In 'balance' mode (default), above_spendable is a non-blocking warning; in 'spendable' mode it is a blocking error.
Typed SDK failures are ConnectSdkError with a stable code. Branch on that instead of parsing message:
tsx
import { ConnectSdkError, ConnectSdkErrorCode } from '@swapped/connect-sdk'
import {
useCoinbaseConnection,
useCoinbaseWithdrawal,
useCoinbaseWithdrawalCooldown,
useOnCoinbaseSessionExpired,
} from '@swapped/connect-sdk/react'
function WithdrawWithErrors() {
const { connect, error: connectError } = useCoinbaseConnection()
const { startWithdrawal, error: withdrawError } = useCoinbaseWithdrawal()
const { isInCooldown, remainingMs } = useCoinbaseWithdrawalCooldown()
useOnCoinbaseSessionExpired(() => {
// Token died on the form — back to Connect
})
async function onStart() {
if (isInCooldown) return
try {
// Builds the request from CoinbaseProvider selection
await startWithdrawal()
} catch (error) {
if (
error instanceof ConnectSdkError &&
error.code === ConnectSdkErrorCode.COINBASE_PASSKEY_NOT_SUPPORTED
) {
// SMS / authenticator, not passkey
}
}
}
const error = connectError ?? withdrawError
let message: string | null = null
if (error instanceof ConnectSdkError) {
switch (error.code) {
case ConnectSdkErrorCode.OAUTH_POPUP_BLOCKED:
message = 'Allow popups for this site, then try connecting again.'
break
case ConnectSdkErrorCode.COINBASE_SESSION_EXPIRED:
message = 'Your Coinbase session expired. Please connect again.'
break
case ConnectSdkErrorCode.COINBASE_PASSKEY_NOT_SUPPORTED:
message =
'Passkey confirmation is not supported. Use SMS or an authenticator app.'
break
case ConnectSdkErrorCode.COINBASE_COOLDOWN_ACTIVE:
message = `Please wait ${Math.ceil(remainingMs / 1000)}s before trying again.`
break
default:
message = error.message
}
} else if (error) {
message = error.message
}
return (
<div>
{message && <p>{message}</p>}
<button type="button" disabled={isInCooldown} onClick={() => void onStart()}>
Withdraw
</button>
<button type="button" onClick={() => void connect()}>
Reconnect
</button>
</div>
)
}When things fail in the flow
| Step | What goes wrong | How you see it | What to do in UI |
|---|---|---|---|
| Connect | Popup blocked or closed | OAUTH_POPUP_* on connection error; useOnOAuthError | Allow popups; retry |
| Connect / later | Token expired | COINBASE_SESSION_EXPIRED or useOnCoinbaseSessionExpired | connect() again |
| Any withdraw call | Never connected | COINBASE_NOT_CONNECTED | Connect first |
| Amount form | Amount below min / invalid / above balance / non-viable above-spendable | Prefer useCoinbaseAmount; startWithdrawal auto-adjusts viable above-spendable | Field error / warning or withdrawal callback |
| Start withdraw | Missing token / amount | COINBASE_SELECTION_INCOMPLETE | Complete selection under CoinbaseProvider |
| Start withdraw | Double-submit | COINBASE_COOLDOWN_ACTIVE | useCoinbaseWithdrawalCooldown |
| Start / confirm | Passkey-only | COINBASE_PASSKEY_NOT_SUPPORTED | SMS / authenticator |
| Confirm 2FA | Wrong code | requires2fa again; hook sets is2faInvalid | Keep 2FA UI; show feedback; clear2faInvalid on edit |
| Confirm 2FA | Nothing pending | COINBASE_NO_ACTIVE_WITHDRAWAL | Start again |
| Start withdraw | Session completed | SESSION_NOT_ACTIVE | restartSession() |
Wrong 2FA keeps status: 'requires2fa'. The React hook sets is2faInvalid for UI feedback (not error / hard failure). Cooldown never blocks confirm.
Error code reference
| Code | When it happens | What to do |
|---|---|---|
OAUTH_POPUP_BLOCKED | Browser blocked login popup | Allow popups; connect() again |
OAUTH_POPUP_CLOSED | Popup closed early | Retry connect() |
COINBASE_NOT_CONNECTED | No OAuth token | Call connect() |
COINBASE_SESSION_EXPIRED | Token invalid | Reconnect; useOnCoinbaseSessionExpired |
REACT_COINBASE_PROVIDER_REQUIRED | Hook used outside CoinbaseProvider | Wrap with CoinbaseProvider |
COINBASE_SELECTION_INCOMPLETE | startWithdrawal() without token/amount | Complete selection first |
COINBASE_INVALID_WITHDRAWAL_AMOUNT | Below min, invalid, or above spendable with no viable cap | Fix amount; see withdrawal auto-adjust |
COINBASE_COOLDOWN_ACTIVE | Start during 30s cooldown | useCoinbaseWithdrawalCooldown |
COINBASE_PASSKEY_NOT_SUPPORTED | Passkey confirmation required | SMS / authenticator 2FA |
COINBASE_NO_ACTIVE_WITHDRAWAL | Confirm with nothing pending | startWithdrawal first |
SESSION_NOT_ACTIVE | Session cannot accept a new payment | restartSession() |
Unmapped HTTP failures surface as API_ERROR. Show a retry message and keep the user on the same step when safe.