# Error handling Handle errors that occur during onramp sessions. For general error handling, see the [Stripe error handling guide](https://docs.stripe.com/error-handling.md). For error codes specific to the crypto onramp, see [Crypto onramp error codes](https://docs.stripe.com/crypto/onramp/embedded-components-error-codes.md). # Web ### Checkout errors When the [checkout API](https://docs.stripe.com/api/crypto/onramp_sessions/checkout.md) returns 200 or 202 but the purchase isn’t done, the response body includes the [CryptoOnrampSession](https://docs.stripe.com/api/crypto/onramp_sessions.md) object with `transaction_details.last_error` set. For SDK integrations, `performCheckout` handles payment next actions such as 3DS internally, so the table below only covers `last_error` values that require explicit action from your integration: | **last\_error** | **Description** | **How to handle** | | ------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | | `missing_kyc` | KYC verification is required. | Have the user complete KYC with `submitKycInfo`. Then call checkout again. | | `missing_document_verification` | Identity document verification is required. | Have the user complete verification with `verifyDocuments`. Then call checkout again. | | `charged_with_expired_quote` | Quote expired. | Call [Refresh a Quote](https://docs.stripe.com/api/crypto/onramp_sessions/quote.md) API, then call checkout again. | | `quote_rate_drifted` | The exchange rate changed since the quote was locked. | Create a new session with an updated quote. | | `transaction_limit_reached` | User’s limit exceeded. | Display an error message. | | `location_not_supported` | User’s location isn’t supported. | Show that the service isn’t available in their region. | | `transaction_failed` | Generic failure. | Display a generic error message. | | `missing_consumer_wallet` | The wallet address doesn’t exist for the current user. | Have the user register the wallet, then call checkout again. | The following pseudocode shows how to handle these cases in a retry loop: ```javascript async function handleCheckout() { // Step 1: Create a new session on your backend before starting checkout. let sessionId = await createSessionOnBackend(); for (let attempt = 0; attempt < 5; attempt++) { try { const result = await onramp.performCheckout(sessionId, async (onrampSessionId: string) => { // Call your back end, which calls POST /v1/crypto/onramp_sessions/{sessionId}/checkout // with the session ID, and return the client_secret from the response. const { client_secret } = await fetch(`/checkout/${onrampSessionId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, }).then(r => r.json()); return client_secret; }); if (result.successful) { // Purchase complete. return; } } catch { // performCheckout threw; fall through to last_error handling below. } // Step 2: Inspect last_error from the session to decide the next action. // This runs whether performCheckout threw or returned { successful: false }. const session = await fetchSessionFromBackend(sessionId); const lastError = session.transaction_details?.last_error; if (lastError === 'missing_kyc') { // Prompt user to provide KYC info, then retry checkout on the same session. await onramp.submitKycInfo({ given_name, surname, id_number, date_of_birth, address }); } else if (lastError === 'missing_document_verification') { // Prompt user to complete identity verification, then retry on the same session. await onramp.verifyDocuments(); } else if (lastError === 'missing_consumer_wallet') { // Prompt user to register a wallet, then retry on the same session. await onramp.registerWalletAddress(walletAddress, network); } else if (lastError === 'charged_with_expired_quote') { // Quote expired. Refresh the quote on your backend, then retry checkout on the same session. await refreshQuoteOnBackend(sessionId); } else if (lastError === 'quote_rate_drifted') { // Exchange rate moved too far. Create a new session with a fresh quote. sessionId = await createSessionOnBackend(); } else { // Terminal errors: transaction_limit_reached, location_not_supported, transaction_failed. // Do not retry. Show an appropriate error message. showError(lastError); return; } } } ```