# Error codes

Every error returns `{ error: { code, message, doc_url, retryable } }`. Retry only when `retryable` is true.

| Code | HTTP | Retry | Meaning | Fix |
|---|---|---|---|---|
| invalid_input | 400 | no | The request body or a field is missing or malformed. | Read `message`, fix the field it names and send again. |
| unauthenticated | 401 | no | No valid key or session was sent, or the key was revoked. | Send `Authorization: Bearer <key>` with an active key. |
| forbidden | 403 | no | You're signed in but your role can't do this. | Ask an org owner. |
| not_found | 404 | no | The thing you asked for doesn't exist or isn't yours. | Check the ID. |
| rate_limited | 429 | yes | Too many requests in a short time. | Wait and retry. Limits are in the `RateLimit-*` headers. |
| invalid_token | 401 | no | A sign-in link or invite is invalid, used or expired. | Request a new link. |
| invalid_code | 400 | no | The phone verification code is wrong or expired. | Request a new code. |
| phone_in_use | 409 | no | This phone number is already verified on another account. | Use a different number or sign in to the other account. |
| email_unverified | 403 | no | The email address isn't verified yet. | Open the sign-in link we emailed. |
| phone_unverified | 403 | no | An org needs a verified phone number first. | Verify your phone, then create the org. |
| ip_not_allowed | 403 | no | The agent key has an IP allowlist and this request came from elsewhere. | Call from an allowed IP or update the allowlist. |
| unknown_tool | 404 | no | No tool with that ID or slug. | List tools with `GET /v1/tools`. |
| unknown_task_type | 400 | no | Not one of the seven task types. | List them with `GET /v1/task-types`. |
| live_execute_unavailable | 501 | no | Live purchases aren't open yet. | Use a test key (`sk_test_…`) until launch. |
| credits_frozen | 403 | no | Arettic has frozen this org's credits while we look into something on the account. | Email support. Reads still work; purchases resume once the freeze is lifted. |
| insufficient_credits | 402 | no | The org's credit balance can't cover this purchase. | Top up credits or lower `max_price`. |
| over_budget | 402 | no | The agent's monthly budget can't cover this purchase. | Raise the budget or wait for the next UTC month. |
| approval_required | 202 | no | The purchase is over the agent's approval threshold or budget. | Retry with `approval_id` after an owner approves. Approvals expire in 24 hours. |
| approval_expired | 409 | no | The approval is older than 24 hours. | Request a new approval. |
| approval_rejected | 403 | no | An owner declined this purchase. | Don't retry it; ask the owner, or change the request. |
| trial_limit | 429 | yes | Orgs that haven't topped up yet can spend up to 50 trial credits an hour. | Wait for the hour to pass, or top up to remove the limit. |
| approval_mismatch | 409 | no | The tool, input or amount differs from what was approved. | Send exactly the approved request, or request a new approval. |
| plan_limit | 402 | no | Your plan's limit on agents, seats or lookups is reached. | Upgrade the plan or wait for the daily reset. |
| not_purchasable | 409 | no | The tool is listed for information only, or isn't live yet, so it can't be bought. | Pick a purchasable tool from POST /v1/recommend (purchasable: true). |
| price_above_max | 402 | no | The price for this request (price × inputs) is above your max_price. | Raise max_price, send fewer inputs, or pick a cheaper tool. |
| tool_paused | 409 | no | The tool is paused (outage, loss-making price or provider issue). | Use `fallback: true` or pick another tool from `recommend`. |
| provider_error | 502 | yes | The provider failed after one retry. Nothing was charged. | Retry later or use `fallback: true`. |
| check_failed | 200 | no | The result didn't pass the published check. Nothing was charged. | Read `reason`; try another tool or fix the input. |
| request_in_progress | 409 | yes | A request with this idempotency_key is still running. | Retry in a few seconds with the same key to get its receipt. |
| provider_quota | 429 | yes | Your org reached today's call limit for this provider. | Use another tool for the task, or wait for 00:00 UTC. |
| aup_limit | 429 | no | The request looks like bulk collection of personal data, which provider terms forbid. | Contact support if this is legitimate research. |
| idempotency_conflict | 409 | no | The idempotency key was reused with a different request. | Use a new key for a new request. |
| reason_required | 400 | no | This action needs a written reason (it goes in the audit log). | Send `reason`. |
| conflict | 409 | no | Something with that slug or email already exists. | Use a different value. |
| unverified_email | 401 | no | Google hasn't verified this email address. | Sign in with an email link instead. |
| invalid_state | 400 | no | The Google sign-in took too long or was opened in another browser. | Start Google sign-in again. |
| google_failed | 401 | yes | Google didn't complete the sign-in. | Try again, or use an email link. |
| not_configured | 503 | no | This feature isn't switched on in this environment. | Use another sign-in method. |
| invalid_amount | 400 | no | A credit amount is zero, negative or otherwise invalid. | Send a positive whole number of credits. |
| capture_exceeds_hold | 409 | no | Internal: a charge was larger than the credits held for it. It was blocked. | Nothing to do on your side; it's logged for us. |
| not_a_hold | 409 | no | Internal: a settlement pointed at something that isn't a hold. It was blocked. | Nothing to do on your side; it's logged for us. |
| already_settled | 409 | no | This purchase was already charged or refunded. | Fetch the receipt instead of retrying. |
| invalid_credentials | 401 | no | Admin sign-in failed: email, password or code is wrong. | Check all three. Five failures lock the account for 15 minutes. |
| locked | 429 | yes | Admin account locked after too many failed sign-ins. | Wait 15 minutes. |
| weak_password | 400 | no | Admin passwords need at least 14 characters. | Use a longer password. |
| resale_rights_missing | 409 | no | Admin: a tool can't be sold until its provider's resale rights are signed. | Record the signed agreement on the provider, or list the tool as info-only. |
| price_missing | 409 | no | Admin: a tool needs a price per success before it can be sold. | Set `price_per_success_credits`. |
| tool_unavailable | 409 | no | The tool can't be used for this right now: it's paused, has no price yet, or has no working provider connection. | Pick another tool, or try again later. |
| adapter_not_ready | 409 | no | Admin: the tool has no working provider connection (no adapter, or its API key isn't set). | Add the provider key to .env, then run npm run providers:check. |
| benchmark_missing | 409 | no | Admin: a tool must be benchmarked in the last 35 days before it can be sold. | Run a benchmark for the tool. |
| below_accuracy_floor | 409 | no | Admin: the tool's latest benchmark accuracy is under the 60% floor. | Keep it info-only, or re-run after the provider improves. |
| not_disputable | 409 | no | The item wasn't charged, so there's nothing to dispute. | Only charged (passed or partial) items can be disputed. |
| dispute_window_closed | 409 | no | Disputes must be opened within 7 days of the purchase. | Contact support if you think this item was charged in error. |
| topup_cap | 402 | no | New accounts can add up to $200 of credits in their first 14 days. | Top up a smaller amount, or wait until the account is 14 days old. |
| confirmation_required | 409 | no | Admin: a large manual adjustment needs its amount confirmed. | Send the same amount again in `confirm_credits`. |
| offer_unavailable | 409 | no | The founding price is used up, has ended, or this org already used it. | Subscribe at the list price, or check GET /v1/pricing for the offer's status. |
| no_saved_card | 409 | no | Auto-reload needs a saved card, and this org has none. | Top up once with `save_card: true`, then turn auto-reload on. |
| payment_unavailable | 502 | yes | The payment provider didn't respond as expected. | Try again in a minute. |
| invalid_signature | 400 | no | A webhook's signature didn't verify, so it was ignored. | Check the webhook signing secret. |
| method_not_allowed | 405 | no | That HTTP method isn't supported here (the MCP endpoint takes POST only). | Use the method in the `Allow` header. |
| fx_unavailable | 503 | yes | No USD→INR exchange rate is available, so INR top-ups are paused. | Pay in USD, or try again later. |
| last_owner | 409 | no | An org must keep at least one owner. | Make someone else an owner first. |
| payload_too_large | 413 | no | The request body is over 1 MB. | Send large batches as async jobs of up to 1,000 items. |
| internal_error | 500 | yes | Something went wrong on our side. | Retry. If it keeps happening, check the status page. |
| network_error | — (SDK-side) | yes | SDK: the request never got a response (DNS, connection reset, offline). | Retry with backoff; the SDKs do this for reads and for `execute`, whose idempotency key makes a retry safe. |
| timeout | — (SDK-side) | yes | SDK: the request hit the client's timeout before a response arrived. | Retry, or raise the client's timeout for long batches (or use jobs for over 25 items). |
| connection_failed | — (SDK-side) | yes | MCP package: the local server couldn't reach the hosted MCP endpoint. | Check ARETTIC_API_URL and your network; the call is retried once if it certainly never left. |
| aborted | — (SDK-side) | no | SDK: your own AbortSignal cancelled the request. | Nothing to fix; send the request again when you want it. |
| http_error | — (SDK-side) | no | SDK: an HTTP error came back without the API's error body (a proxy, firewall or load balancer answered). The HTTP status is on the error. | Check ARETTIC_API_URL and anything between you and the API. A 429 or 5xx from a proxy is reported as `rate_limited` or `internal_error` and retried. |
| unexpected_redirect | — (SDK-side) | no | SDK: the server answered with a redirect, which the SDKs never follow (it could leak your key). | Check ARETTIC_API_URL: it should be https://api.arettic.com with no path. |

This page as HTML: https://arettic.com/docs/errors · Markdown: https://arettic.com/docs/errors.md · JSON: https://arettic.com/docs/errors.json
