# OneDex MCP: errors and recovery

Endpoint: `https://aggregator.onedex.app/ai/mcp` · Docs: https://onedex.app/agents · Agent rules: https://onedex.app/agents/agent-instructions.md

A tool error is a normal MCP tool result with `isError: true`. Its text is JSON:

```json
{ "error": { "code": "slippage_needs_confirmation", "message": "Slippage 8% is above 5%: pass confirmHighSlippage: true to accept it (max 20%)" } }
```

Read `error.code` to decide what to do; show `error.message` to the user when it helps. Messages never contain upstream text.

## Error codes

| Code | Meaning | What the agent should do | Auto-retry |
|---|---|---|---|
| `invalid_request` | Malformed input: amount not a decimal string or not above zero, bad `erd1…` address, `sender` is a smart contract, bad transaction hash, `tokenIn` = `tokenOut`, or an EGLD ↔ WEGLD wrap | Fix the input (amount like `"1.5"`, the wallet's own `erd1…` address, 64-hex hash). Ask the user if the intent is unclear. | No |
| `unknown_token` | Token not tradable through the OneDex aggregator, or not found | Call `list_tokens` with `search`, then confirm the identifier with the user. | No |
| `ambiguous_token` | Ticker matches several tokens; the message lists the identifiers | Show the identifiers and let the user choose. Never pick one yourself. | No |
| `no_route` | No route for this pair and amount (also: zero quoted output, or a route that looked unrealistic for a tiny amount) | Tell the user. A different amount may work, but only the user decides the amount. | No |
| `slippage_needs_confirmation` | `slippageBps` above 500 (5%) without `confirmHighSlippage: true` | Show the message and ask the user. Only after an explicit yes, call again with `confirmHighSlippage: true`. | No |
| `slippage_refused` | `slippageBps` from 2001 to 10000 (above 20%). Values above 10000 (or below 1, or not an integer) fail the input schema instead (see below) | Use at most 2000. Suggest a smaller amount instead. | No |
| `impact_needs_confirmation` | Price impact above 10%, the aggregator-based and independent impacts disagree by more than 5 points, or the impact is unknown on a XOXNO-only route (no OneDex route and no USD prices), without `confirmHighImpact: true` | Show the reasons in the message. Suggest a smaller amount. Only after an explicit yes, call again with `confirmHighImpact: true`. | No |
| `unsafe_min_out` | Minimum received would be zero (amount too small) or above the quoted output | Do not swap. Tell the user; a larger amount may be needed (user decides). | No |
| `insufficient_balance` | `build_swap_tx` only. EGLD input: the EGLD balance must cover the amount + the max network fee. ESDT input: the token amount and the EGLD fee are checked separately. The message says needed vs available | Tell the user. Check `get_wallet_balances`; leave EGLD for the fee. | No |
| `tx_rejected` | The transaction failed verification and was not returned (`build_swap_tx`) or not simulated (`simulate_swap_tx`, e.g. a field was changed) | Do not sign anything. Pass the `transaction` from `build_swap_tx` unchanged; otherwise quote and build again. Report it if it repeats. | No |
| `rate_limited` | Per-IP tool limit reached (see Rate limits below); the message says `retry in Ns` | Wait the number of seconds in the message, then retry. Bounded: give up after a few attempts and tell the user. | Yes, bounded, after the given wait |
| `busy` | Too many calls from this client already queued, or no upstream slot within 8 s; the message says `retry in a few seconds` | Wait a few seconds with exponential backoff and jitter, then retry. Bounded. Lower your concurrency. | Yes, bounded backoff |
| `upstream` | An upstream service (OneDex aggregator, XOXNO, MultiversX API or gateway) failed, timed out or returned invalid data | Retry with a bounded backoff. All six tools are read-only (none signs or broadcasts). If a retried `build_swap_tx` returns a new transaction, show and check it again before anything is signed. | Yes, bounded (tool calls only) |
| `network_unavailable` | Swaps are not configured on the requested network (e.g. `devnet` on the public server) | Use `network: "mainnet"` or omit `network`. | No |
| `internal_error` | Unexpected server error; no details are exposed | Retry once after a short wait. If it persists, stop and tell the user. | Once |

**Never auto-retry signing.** Signing and broadcasting happen outside the server. Re-broadcasting the SAME signed transaction is harmless (same nonce); signing a NEW one for a swap whose outcome is unknown is the danger. After a broadcast timeout or a lost response, check `tx_status` with the hash and the account nonce before anything else.

## Other failures (no error code)

| Situation | What you get | What to do |
|---|---|---|
| Arguments do not match the tool's input schema (e.g. `slippageBps` above 10000) | `isError: true` with plain text starting `MCP error -32602: Input validation error` (not the JSON above) | Fix the arguments using the schema from `tools/list`. Do not retry unchanged. |
| More than 120 HTTP requests per minute from your IP to `/ai/mcp` | HTTP 429, JSON-RPC error `Rate limit reached, retry in Ns`, `Retry-After` header | Wait `Retry-After` seconds. Bounded retry. |
| `build_swap_tx` returns a `transaction` without `nonce` | A normal result with the warning `Could not read the sender account: nonce not set, balance not checked` | Do not sign it (`simulate_swap_tx` also refuses it: `nonce is required`). Build again shortly, or a person uses `signingLink` (the wallet sets the nonce). The balance was not checked either: the wallet rejects an underfunded transaction, but tell the user. |
| `simulate_swap_tx` returns `status: "fail"` or `meetsMinOut: false` | A normal result (`failReason` explains a fail) | Do not sign. Show `failReason`; quote and build again if the market moved. |
| `simulate_swap_tx` returns `meetsMinOut: "—"` | A normal result: the received amount could not be read | Autonomous agent: stop, do not sign. Human flow: show it to the user and let them decide. |
| `tx_status` returns `status: "not_found"` | A normal result: not indexed yet, or unknown hash | Wait a few seconds and check again. Never sign a new transaction for this swap. |

## Rate limits

Per client IP, fixed one-minute windows, counted by each server instance. Hosted connectors (Claude.ai, ChatGPT) call from the vendor's shared IPs, so `rate_limited` is more likely there than from your own client.

| Scope | Limit | Over the limit |
|---|---|---|
| HTTP requests to `/ai/mcp` | 120 per minute | HTTP 429 with `Retry-After` |
| `list_tokens`, `get_quote`, `get_wallet_balances`, `tx_status` (one shared bucket) | 30 calls per minute | Tool error `rate_limited` with the wait in seconds |
| `build_swap_tx`, `simulate_swap_tx` (one shared bucket) | 10 calls per minute | Tool error `rate_limited` with the wait in seconds |
