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

# Transaction Failure Handling: Error Codes and UI Patterns

> Learn how to handle every transaction failure scenario in SocketFi — from user rejections to contract errors — with clear error codes and UI patterns.

Not every transaction completes successfully, and that's expected. Users cancel actions, wallet policies reject requests, contracts throw errors, and networks experience transient issues. Robust failure handling isn't an edge case concern — it's a core part of building a trustworthy transaction experience. This page covers the common failure scenarios you'll encounter, the error codes you'll receive, and the patterns you should use to surface them clearly to users.

<Warning>
  A resolved promise from `requestTransaction()` does not guarantee success. Always check `result.success` before updating any application state. If `result.success` is `false`, no on-chain state changed — the user's wallet, balances, and positions are exactly as they were before the call.
</Warning>

## Common failure scenarios

<CardGroup cols={2}>
  <Card title="User Rejection" icon="hand">
    The user saw the approval screen and chose not to approve. This is expected, normal user behavior — not an error. Reset your UI gracefully without showing an error message.
  </Card>

  <Card title="Policy Violation" icon="shield-halved">
    The user approved, but the smart wallet's policy blocked execution — for example, a spending limit was exceeded or the contract isn't on the wallet's allowlist.
  </Card>

  <Card title="Contract Execution Error" icon="bug">
    The transaction was authorized and submitted, but the Soroban contract panicked or returned an error during execution. No state changes were applied.
  </Card>

  <Card title="Network / Submission Error" icon="wifi-slash">
    The transaction failed to reach or be processed by the network due to connectivity issues, RPC timeouts, or ledger congestion. These are often transient — a retry may succeed.
  </Card>

  <Card title="Fee Failure" icon="coins">
    The wallet's fee evaluation returned `CannotProceed` — the fee asset isn't supported, the calculated fee exceeds `max_total_fee`, or the deferred fee balance has reached its cap.
  </Card>

  <Card title="Authentication / Session Error" icon="lock">
    The user's session token has expired or is invalid. The user needs to re-authenticate before attempting any transaction.
  </Card>
</CardGroup>

## Error codes

SocketFi throws structured errors with a `code` property that lets you handle specific failure scenarios programmatically:

| Code                    | When it occurs                                                    | Recommended response                                 |
| ----------------------- | ----------------------------------------------------------------- | ---------------------------------------------------- |
| `USER_CANCELLED`        | User dismissed the approval screen                                | Silently reset to idle — no error message needed     |
| `TRANSACTION_REJECTED`  | Wallet authorization failed (policy violation, invalid signature) | Surface a descriptive message; check wallet policies |
| `AUTHENTICATION_FAILED` | Passkey verification failed                                       | Prompt re-authentication                             |
| `TOKEN_EXPIRED`         | The user's session token has expired                              | Redirect to sign-in or trigger session refresh       |
| `INVALID_TOKEN`         | The session token is malformed or unrecognized                    | Clear session state and redirect to sign-in          |
| `INVALID_SIGNATURE`     | The cryptographic signature on the transaction is invalid         | Log for investigation; prompt the user to retry      |
| `POPUP_BLOCKED`         | The browser blocked the approval popup                            | Instruct user to allow popups and retry              |

## Complete error handling example

The following example demonstrates handling multiple error types from a single `requestTransaction()` call:

```ts theme={null}
async function executeTransfer(recipient: string, amount: bigint) {
  try {
    const result = await socketfi.requestTransaction({
      contractId: TOKEN_CONTRACT,
      method: "transfer",
      args: [recipient, amount],
    });

    if (result.success) {
      // Safe to update application state
      refreshBalance();
      showSuccess(`Transfer complete. Hash: ${result.transactionHash}`);
      return;
    }

    // Resolved but not successful — no state change occurred
    showError("The transfer could not be completed. Please try again.");

  } catch (error) {
    switch (error.code) {
      case "USER_CANCELLED":
        // User made an intentional choice — reset quietly
        resetToIdle();
        break;

      case "TRANSACTION_REJECTED":
        showError(
          "Your wallet rejected this transaction. " +
          "This may be caused by a spending limit or contract policy."
        );
        break;

      case "TOKEN_EXPIRED":
      case "INVALID_TOKEN":
        showError("Your session has expired. Please sign in again.");
        redirectToSignIn();
        break;

      case "POPUP_BLOCKED":
        showError(
          "The approval window was blocked. " +
          "Please allow popups for this site and try again."
        );
        break;

      case "AUTHENTICATION_FAILED":
        showError("Authentication failed. Please try again.");
        break;

      default:
        showError("An unexpected error occurred. Please try again.");
        console.error("[SocketFi]", error);
    }
  }
}
```

## UI patterns for failures

Good failure UX shares three properties: it explains what happened in plain language, it offers a clear path forward, and it preserves the user's context so they don't have to start over.

**Explain, don't expose.** Translate error codes into user-facing language. "Your wallet rejected this transaction due to a policy limit" is more useful than "TRANSACTION\_REJECTED." Reserve raw error codes for developer logs.

**Distinguish rejection from failure.** A user who cancels the approval screen made an intentional choice. Don't show them a red error banner — just reset the form to its ready state. Reserve error messaging for genuine failures.

**Offer retry when appropriate.** Network errors and transient fee issues are often retryable. Contract errors and policy violations usually aren't — the underlying condition needs to change before a retry will succeed. Only show a retry button when there's a reasonable expectation it will help.

**Preserve form state.** If a transfer fails because the popup was blocked, the user shouldn't have to re-enter the recipient address and amount. Keep form values intact when transitioning to an error state.

```tsx theme={null}
// Example: error state with retry
function TransactionErrorState({
  errorCode,
  onRetry,
  onDismiss,
}: {
  errorCode: string;
  onRetry?: () => void;
  onDismiss: () => void;
}) {
  const isRetryable = !["TOKEN_EXPIRED", "INVALID_TOKEN", "USER_CANCELLED"].includes(errorCode);

  const message = getFriendlyErrorMessage(errorCode); // your mapping function

  return (
    <div>
      <p>{message}</p>
      {isRetryable && onRetry && (
        <button onClick={onRetry}>Try again</button>
      )}
      <button onClick={onDismiss}>Dismiss</button>
    </div>
  );
}
```

## Handling failures at each lifecycle stage

Different failure types occur at different points in the [transaction lifecycle](/transactions/transaction-lifecycle). Understanding where a failure originates helps you craft the right response:

| Stage             | Failure type      | How to respond                                           |
| ----------------- | ----------------- | -------------------------------------------------------- |
| Awaiting Approval | User cancels      | Silent reset                                             |
| Awaiting Approval | Popup blocked     | Show guidance to allow popups                            |
| Authorized        | Policy violation  | Explain policy; suggest checking wallet settings         |
| Authorized        | Invalid signature | Prompt retry; log for investigation                      |
| Submitting        | Network error     | Offer retry after a brief delay                          |
| Executing         | Contract error    | Show descriptive message; check contract conditions      |
| Fee evaluation    | CannotProceed     | Explain fee issue; guide user to settle deferred balance |

<Note>
  When `result.success` is `false`, you can't always determine the exact cause from the `TransactionResult` alone — use `try/catch` to capture thrown errors with specific `code` values, which provide more precise failure information for your error handling logic.
</Note>
