# Errors

This is the canonical list of error codes the Arcade Cafe API returns and the recommended agent reaction for each. Codes are stable; HTTP status is the secondary signal.

## REST Error Envelope

```json
{
  "error": {
    "code": "GAME_INPUT_REJECTED",
    "message": "Bet exceeds balance.",
    "details": {}
  }
}
```

`details` is optional. `402` payment errors also include x402 negotiation fields (`accepts`, `x402Version`) at the top level so the client can resubmit with a valid payment payload.

## MCP Error Envelope

MCP tool calls that fail at the tool layer return:

```json
{
  "error": "Agent bet exceeds 0.05 USDC.",
  "limits": { "maxBet": 0.05, "maxWagered": 1, "wagered": 0.04 }
}
```

MCP tool calls that fail at the underlying HTTP layer (paid plays, x402 negotiation) preserve the REST `{ error: { code, message, details? } }` envelope inside `structuredContent` and set `isError: true` on the tool result.

## Codes

| Code | HTTP | Where | Meaning | What the agent should do |
| --- | --- | --- | --- | --- |
| `MISSING_WALLET` | 400 | `GET /api/wallet/challenge` | `wallet` query parameter was omitted. | Resend with `?wallet=<address>`. |
| `INVALID_WALLET_ADDRESS` | 400 | wallet routes | Value is not a valid Solana public key. | Stop and surface the address to the user; do not retry. |
| `INVALID_WALLET_SESSION_REQUEST` | 400 | `POST /api/wallet/session` | Body is missing `walletAddress`, `message`, or `signature`. | Resend a complete body. |
| `WALLET_SESSION_REJECTED` | 401 | `POST /api/wallet/session` | Signature did not verify or the challenge expired. | Fetch a new challenge and re-sign. |
| `WALLET_SESSION_REQUIRED` | 401 | any wallet-owned route | Bearer wallet-session token is missing or expired. | Re-run the wallet-challenge → wallet-session flow and resend with `Authorization: Bearer …`. |
| `X402_PAYMENT_REQUIRED` | 402 | paid play routes | No `payment-signature` on a paid route. Response body includes `accepts` and `x402Version`. | Build an x402 payload for the listed `accepts[0]` and resend with `payment-signature: <payload>`. |
| `X402_PAYMENT_VERIFICATION_FAILED` | 402 | paid play routes | Facilitator rejected `verify`. | Do not retry the same payload. Mint a fresh x402 payment and resubmit. |
| `X402_PAYMENT_SETTLEMENT_FAILED` | 402 | paid play routes | Facilitator rejected `settle` after a passing `verify`. | Same as above — fresh payment, fresh request. Treat the original wager as not accepted. |
| `X402_PAYER_MISMATCH` | 403 | paid play routes | The x402 payer wallet does not match the bearer wallet session. | Stop. The session and payment must be for the same wallet. |
| `X402_CONFIG_ERROR` | 500 | paid play routes | Server is misconfigured. | Stop; this is not a client problem. |
| `INVALID_BET_AMOUNT` | 400 | Protocol V1 play (`prepare`) and `x402-deposit` | `wager`/`amount` is not a positive finite number. | Fix the request body; do not resubmit until the value is valid. |
| `GAME_INPUT_REJECTED` | 400 | game routes | Game engine rejected the play. Common reasons: bet exceeds balance, possible payout exceeds bankroll `maxPayout`, sweeper ticket already active, out-of-order sweeper choice, no active ticket to cash out. | Read `message`. Most cases require changing inputs (lower bet, different mode) or refreshing session state. Do not blind-retry. |
| `RECEIPT_NOT_FOUND` | 404 | `GET /api/receipts/:id` | No receipt exists for that id. | Stop polling this id. If the id came from a paid call that timed out, the play likely did not settle; re-check after a short delay before assuming loss. |
| `MCP_PAYMENT_FAILED` | — | `/api/mcp` | MCP could not parse the inner HTTP error envelope on a paid tool call. | Treat as `X402_PAYMENT_VERIFICATION_FAILED`. Mint a fresh payment. |

## Agent Tool Limits

The MCP tool layer also enforces per-wallet agent caps. These do not use the coded REST envelope; they return the MCP envelope above with HTTP-equivalent status:

- `400` `Agent bet exceeds 0.05 USDC.` — reduce `betAmount` to ≤ `limits.maxBet`.
- `429` `Agent wager limit reached.` — wait for the limit window to roll over or fall back to read-only tools. The current `limits.wagered` is returned with the error so the agent can see how close it was.

Call `get_arcade_state` to read live `limits` before sizing a paid play.

## Retry Rules

- **Never auto-retry a paid call after a network error.** The play may have settled. Re-check `GET /api/receipts/:id` with any receipt id you saw, or call `get_arcade_state` and inspect recent activity, before sending a second wager.
- **Always mint a fresh x402 payment for any retry.** A facilitator that has already seen a payment payload will reject a duplicate.
- **`GAME_INPUT_REJECTED` is terminal for that input.** Treat it as a validation failure, not a transient error.
- **`401` / `403` are terminal for that token.** Re-authenticate; do not loop.
