Skip to main content
A Flow payment can go wrong in two ways. A function call can be rejected, which throws an error and usually leaves the flow unchanged. Or the flow itself can fail later, after the payment was sent, which the flow records in failure instead of throwing. This page covers both. Every error the SDK throws extends BaseError and has a code. Check the class with instanceof, and use error.code to tell cases of the same class apart. Do not match on error.message, because its text can change. Import every class from @dynamic-labs-sdk/client.

Error classes

The sections below describe when each one happens and what your UI should do.

FlowNotFoundError

The flow does not exist, or it belongs to a different environment than the Dynamic client’s. Retrying does not help. Check the flow ID and the client’s environmentId, and show a “payment not found” screen. The functions that need a session token report an unknown flow as FlowSessionExpiredError instead.

FlowSourceBlockedError

Screening blocked the payment source. reason says which check blocked it:
  • sanctions_blocked: the wallet failed screening when it was attached, or when a deposit mode quote changed the amount. When attaching, nothing is saved, so the payer can attach another wallet or choose another source.
  • risk_blocked: a quote found the flow blocked by risk screening. The payer can attach a different source on the same flow.
Tell the payer this wallet cannot be used for the payment, and do not retry the same wallet. Screening describes what is screened and when.

FlowExchangeSourceError

The exchange cannot be attached as the payment source. retryable is true only for exchange_unavailable. List only the exchanges that getFlowSupportedExchanges returns to avoid most of these errors.

FlowQuoteAmountError

The quote rejected the amount the payer chose. This applies only to deposit mode, where the payer can change the amount.
  • amount_invalid: the amount is malformed, too precise, or too small. Use at most 2 decimals for USD and at most the token’s decimals for SOURCE_TOKEN. Ask the payer for a valid amount.
  • amount_not_editable: the flow’s amount is fixed, because the flow is not in deposit mode. Stop sending amount for this flow.

FlowQuoteUnavailableError

No route was found to any token the flow settles in, so the quote failed. quoteFailures explains why each settlement token could not be quoted, and Quote failures lists the reasons.

FlowPrepareSigningError

Preparing the wallet transaction failed before anything was signed or sent.
  • quote_expired: the quote expired, which happens 60 seconds after a wallet quote. Get a new quote with getFlowQuote, then let the payer confirm again.
  • insufficient_balance: the wallet cannot cover the transfer amount or the gas. Ask the payer to fund the wallet or pay with another token.

FlowStateConflictError

The action conflicts with the flow’s current state. code carries the reason, and retryable is true only for flow_concurrent_update. New codes can be added, so treat an unknown code like flow_invalid_transition. The React mutation hooks refetch the flow on this error, so useGetFlow shows the real state.

FlowSessionTokenMissingError and FlowSessionExpiredError

Every action after attaching a source needs the flow’s session token, which the SDK stores on the device when the source is attached. The two errors differ in whether a token was stored:
  • FlowSessionTokenMissingError: no token for the flow is stored on this device. The source was attached on another device, the payer cleared storage, or no source was attached yet.
  • FlowSessionExpiredError: a token was stored, but the server no longer accepts it. The token was revoked, expired, or replaced when a source was attached again on another device, or the flow ended. The SDK removes the stored token before it throws.
Both recover the same way. Call getFlow. If the flow exists and the payment has not been sent, attach the source again, which stores a new token on this device. Otherwise, show the flow’s state. Reload and another device shows the code.

UserRejectedError

The payer declined a wallet prompt during submitFlowTransaction. Nothing was sent, and the SDK requested a new quote with the same token and chain before it threw, so the flow is ready for another attempt. Tell the payer the payment was not approved, and let them submit again or choose another way to pay. Do not cancel the flow.

InvalidParamError

The wallet’s chain cannot sign Flow payments. submitFlowTransaction supports wallets on EVM, Solana, Bitcoin, Sui, and TRON. Ask the payer to use a wallet on one of those chains.

Quote failures

FlowQuoteUnavailableError.quoteFailures has one entry for each settlement token that could not be quoted, with the token’s chainId, chainName, and symbol, and a reason:

Errors without a class

Some rejections have no dedicated class. Show error.message for them, and let the payer change their choice:
  • The flow sets disableSwaps, and the payer’s token and chain do not match a settlement. Ask the payer to pay with a token and chain you settle in.
  • The payer’s token cannot be priced.
  • A deposit address is not available for the chosen chain and token.
  • The SDK could not get the transaction to sign during submitFlowTransaction. Nothing was sent; read the flow and let the payer confirm again from quoted.

When to retry

Most errors leave the flow unchanged, but after the payer approves a wallet payment, an error does not prove that nothing was sent. Follow these rules for submitFlowTransaction:
  • After UserRejectedError, the payer can submit again. The SDK already got a new quote.
  • After FlowPrepareSigningError with code quote_expired, get a new quote, then let the payer submit again.
  • After any other error, do not retry. Call getFlow and show the screen for its state. A flow still in quoted was not sent, and the payer can confirm again. A flow in signing or later may have been sent, so show it as processing. A second approval could send a second payment.
For other functions, retry only when the error says it can succeed: FlowStateConflictError with retryable (after reading the flow), FlowExchangeSourceError with retryable, and quote failures whose reason is provider_rate_limited or provider_unavailable.

Failures after the payment is sent

Once the payment is sent, nothing throws. If the payment or its delivery fails, the flow’s executionState or settlementState becomes failed, and flow.failure describes why:
A failed flow cannot be retried. A new attempt needs a new flow from your server. Failures and refunds lists every failure code, what it means, and what to show the payer.
Last modified on October 28, 2026