> ## Documentation Index
> Fetch the complete documentation index at: https://docs.b3.fun/llms.txt
> Use this file to discover all available pages before exploring further.

# Error handling

> Order statuses, error codes, and patterns for handling failures in AnySpend

## Order status lifecycle

Every AnySpend order moves through these states:

```typescript title="Order statuses" icon="list" theme={null}
enum OrderStatus {
  // Waiting
  SCANNING_DEPOSIT_TRANSACTION = "scanning_deposit_transaction",
  WAITING_STRIPE_PAYMENT = "waiting_stripe_payment",
  EXPIRED = "expired",

  // Processing
  SENDING_TOKEN_FROM_VAULT = "sending_token_from_vault",
  RELAY = "relay",

  // Done
  EXECUTED = "executed",

  // Failed
  REFUNDING = "refunding",
  REFUNDED = "refunded",
  FAILURE = "failure",
}
```

| Status                         | What's happening                     | User action                      |
| ------------------------------ | ------------------------------------ | -------------------------------- |
| `scanning_deposit_transaction` | Waiting for blockchain confirmation  | None -- wait                     |
| `waiting_stripe_payment`       | Processing card payment              | May need to complete 3D Secure   |
| `sending_token_from_vault`     | Sending tokens for swap              | None -- automatic                |
| `relay`                        | Cross-chain transaction in progress  | None -- wait                     |
| `executed`                     | Done                                 | None                             |
| `expired`                      | Order expired before payment arrived | Create a new order               |
| `refunding`                    | Automatic refund in progress         | None -- wait                     |
| `refunded`                     | Refund sent                          | Check wallet for refunded tokens |
| `failure`                      | Transaction failed                   | Review error details, retry      |

## Error codes

### Payment errors

<AccordionGroup>
  <Accordion title="INSUFFICIENT_BALANCE">
    User doesn't have enough tokens. Prompt them to add funds or switch to a different token.

    ```typescript theme={null}
    if (error.message === "INSUFFICIENT_BALANCE") {
      toast.error("Not enough funds. Add tokens or choose a different payment method.");
    }
    ```
  </Accordion>

  <Accordion title="INVALID_TOKEN_ADDRESS">
    Token contract isn't supported on the target chain. Show the user which tokens are available.

    ```typescript theme={null}
    if (error.message === "INVALID_TOKEN_ADDRESS") {
      toast.error("This token isn't supported. Please choose another.");
    }
    ```
  </Accordion>

  <Accordion title="MINIMUM_AMOUNT_NOT_MET">
    Transaction amount is below the minimum threshold.

    ```typescript theme={null}
    if (error.message === "MINIMUM_AMOUNT_NOT_MET") {
      toast.error(`Minimum amount is $${minimumAmount}.`);
    }
    ```
  </Accordion>

  <Accordion title="MAXIMUM_AMOUNT_EXCEEDED">
    Transaction amount exceeds the limit. Split into multiple transactions or reduce the amount.

    ```typescript theme={null}
    if (error.message === "MAXIMUM_AMOUNT_EXCEEDED") {
      toast.error(`Maximum amount is $${maximumAmount}.`);
    }
    ```
  </Accordion>
</AccordionGroup>

### Network errors

<AccordionGroup>
  <Accordion title="SLIPPAGE">
    Price moved beyond tolerance during execution. Retry or wait for the market to settle.

    ```typescript theme={null}
    if (error.message === "SLIPPAGE") {
      toast.warning("Price moved. Retrying...");
      retryWithHigherSlippage();
    }
    ```
  </Accordion>

  <Accordion title="NETWORK_ERROR">
    RPC connection issue or chain congestion. Retry after a short delay.

    ```typescript theme={null}
    if (error.message === "NETWORK_ERROR") {
      toast.error("Network issue. Check your connection and try again.");
    }
    ```
  </Accordion>

  <Accordion title="QUOTE_EXPIRED">
    Price quote is stale. Fetch a new one.

    ```typescript theme={null}
    if (error.message === "QUOTE_EXPIRED") {
      toast.info("Quote expired. Getting a fresh one...");
      refreshQuoteAndRetry();
    }
    ```
  </Accordion>

  <Accordion title="CHAIN_NOT_SUPPORTED">
    Requested chain isn't supported. Show the user which chains are available.

    ```typescript theme={null}
    if (error.message === "CHAIN_NOT_SUPPORTED") {
      toast.error("This chain isn't supported.");
      showSupportedChains();
    }
    ```
  </Accordion>
</AccordionGroup>

### Contract errors

<AccordionGroup>
  <Accordion title="CONTRACT_CALL_FAILED">
    Smart contract execution failed. Double-check the contract parameters and state.
  </Accordion>

  <Accordion title="INSUFFICIENT_GAS">
    Gas limit was too low. Retry with a higher limit.
  </Accordion>

  <Accordion title="NONCE_TOO_LOW">
    Nonce conflict from a pending transaction. Wait for the pending tx to confirm, then retry.
  </Accordion>

  <Accordion title="TRANSACTION_REVERTED">
    The contract reverted. Usually means a precondition wasn't met (e.g., sale ended, not enough allowance).
  </Accordion>
</AccordionGroup>

## Error handling patterns

### Per-component error handling

```tsx title="Payment with error handling" icon="shield-exclamation" theme={null}
import { useAnyspendCreateOrder } from "@b3dotfun/sdk/anyspend";

function PaymentComponent() {
  const [error, setError] = useState<string | null>(null);
  const [retryCount, setRetryCount] = useState(0);

  const { createOrder, isCreatingOrder } = useAnyspendCreateOrder({
    onError: (error) => {
      switch (error.message) {
        case "INSUFFICIENT_BALANCE":
          setError("Not enough funds. Add tokens or try a different payment method.");
          break;
        case "SLIPPAGE":
          if (retryCount < 3) {
            setError("Price moved. Retrying...");
            setTimeout(() => {
              setRetryCount((prev) => prev + 1);
              retryPayment();
            }, 2000);
          } else {
            setError("Price too volatile right now. Try again later.");
          }
          break;
        case "QUOTE_EXPIRED":
          setError("Quote expired. Getting a fresh one...");
          refreshQuote();
          break;
        default:
          setError("Payment failed. Try again or contact support.");
      }
    },
    onSuccess: () => {
      setError(null);
      setRetryCount(0);
    },
  });

  return (
    <div>
      {error && (
        <div className="error-banner">
          <span>{error}</span>
          <button onClick={() => setError(null)}>Dismiss</button>
        </div>
      )}
      <button onClick={handlePayment} disabled={isCreatingOrder}>
        {isCreatingOrder ? "Processing..." : "Pay Now"}
      </button>
    </div>
  );
}
```

### Order status monitoring

```tsx title="Order tracker" icon="eye" theme={null}
import { useAnyspendOrderAndTransactions } from "@b3dotfun/sdk/anyspend";

function OrderTracker({ orderId }: { orderId: string }) {
  const { orderAndTransactions, isLoadingOrderAndTransactions, getOrderAndTransactionsError } =
    useAnyspendOrderAndTransactions(orderId);

  if (getOrderAndTransactionsError) {
    return (
      <div>
        <p>Couldn't load order status. Check your connection and try again.</p>
        <button onClick={() => window.location.reload()}>Retry</button>
      </div>
    );
  }

  if (!orderAndTransactions) return <div>Loading order status...</div>;

  const { order, executeTx, refundTxs } = orderAndTransactions.data;

  switch (order.status) {
    case "scanning_deposit_transaction":
      return <p>Waiting for payment confirmation (1-2 minutes)...</p>;
    case "relay":
      return <p>Processing cross-chain transaction...</p>;
    case "executed":
      return (
        <div>
          <p>Done!</p>
          {executeTx && (
            <a href={`https://basescan.org/tx/${executeTx.txHash}`} target="_blank">
              View transaction
            </a>
          )}
        </div>
      );
    case "failure":
    case "obtain_failed":
      return (
        <div>
          <p>Transaction failed: {order.errorDetails || "Unknown error"}</p>
          <button onClick={() => createNewOrder()}>Try again</button>
        </div>
      );
    case "refunded":
      return <p>Refund processed. Check your wallet.</p>;
    case "expired":
      return (
        <div>
          <p>Order expired before payment arrived.</p>
          <button onClick={() => createNewOrder()}>Create new order</button>
        </div>
      );
    default:
      return <p>Processing... (status: {order.status})</p>;
  }
}
```

### Error boundary

Wrap AnySpend components in an error boundary to catch rendering crashes:

```tsx title="Error boundary" icon="shield" theme={null}
import React, { Component, ErrorInfo } from "react";

class AnySpendErrorBoundary extends Component<
  { children: React.ReactNode },
  { hasError: boolean; error?: Error }
> {
  state = { hasError: false, error: undefined as Error | undefined };

  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error("AnySpend error:", error, errorInfo);
    // Report to your error tracking service (Sentry, etc.)
  }

  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h3>Something went wrong</h3>
          <p>{this.state.error?.message}</p>
          <button onClick={() => this.setState({ hasError: false })}>Try again</button>
        </div>
      );
    }
    return this.props.children;
  }
}

// Usage
function App() {
  return (
    <AnySpendErrorBoundary>
      <AnyspendProvider>
        <YourApp />
      </AnyspendProvider>
    </AnySpendErrorBoundary>
  );
}
```

## User-friendly error messages

A helper that maps error codes to messages your users can actually understand:

```tsx title="Error message helper" icon="comment" theme={null}
function getErrorMessage(error: Error) {
  const map: Record<string, { title: string; message: string; action?: string }> = {
    INSUFFICIENT_BALANCE: {
      title: "Insufficient balance",
      message: "You don't have enough funds for this transaction.",
      action: "Add funds or choose a different payment method.",
    },
    SLIPPAGE: {
      title: "Price changed",
      message: "The price moved while processing your transaction.",
      action: "We'll retry with updated pricing.",
    },
    NETWORK_ERROR: {
      title: "Connection issue",
      message: "Can't reach the blockchain network.",
      action: "Check your internet connection and try again.",
    },
    QUOTE_EXPIRED: {
      title: "Quote expired",
      message: "The price quote is no longer valid.",
      action: "Getting a fresh quote...",
    },
  };

  return map[error.message] ?? {
    title: "Transaction failed",
    message: "Something went wrong.",
    action: "Try again or contact support.",
  };
}
```

## Tips

* **Keep users informed** during long operations. Cross-chain transactions can take a few minutes -- show progress, not just a spinner.
* **Retry transient errors** (slippage, network, quote expired) with exponential backoff. Many resolve on the second try.
* **Log errors** with context (order ID, user address, timestamp) so you can debug issues after the fact.
* **Offer fallbacks** when possible. If cross-chain fails, suggest same-chain. If crypto fails, suggest fiat onramp.

## Getting help

<CardGroup cols={2}>
  <Card title="Discord" icon="discord" href="https://discord.gg/b3dotfun">
    Get help from the community and support team
  </Card>

  <Card title="GitHub" icon="github" href="https://github.com/b3-fun/b3/issues">
    Report bugs and request features
  </Card>
</CardGroup>

<Info>
  When reporting issues, include: error message/code, steps to reproduce, browser/device info, and order ID if applicable.
</Info>
