This page lists the errors you can get back from IXO APIs and the shape they come in. Use it to decide, from the status code and the response body, whether to fix the request, wait and retry, or report the failure.
It covers the HTTP status codes, the JSON error body, the error envelope and rate-limit headers of
the routes on docs.ixo.world, and the separate error shape of its MCP server.
Status codes at a glance
| Class | Code | Meaning |
|---|---|---|
| Client error (4xx): the request needs to change | 400 Bad Request | Malformed request or invalid parameters |
| 401 Unauthorized | Authentication required or failed | |
| 403 Forbidden | Insufficient permissions | |
| 404 Not Found | Resource not found | |
| 429 Too Many Requests | Rate limit exceeded | |
| Server error (5xx): the server could not answer | 500 Internal Server Error | Unexpected server condition |
| 502 Bad Gateway | Invalid response from upstream | |
| 503 Service Unavailable | Server temporarily unable to handle request |
Error response structure
IXO APIs return error responses as JSON:
{
"error": {
"code": 400,
"message": "Invalid request parameters",
"details": [
{
"field": "entityId",
"error": "Missing required parameter"
}
]
}
}Response fields
| Field | Meaning |
|---|---|
code | HTTP status code |
message | Human-readable error description |
details | Additional error information |
Docs API errors
The /api/* routes on docs.ixo.world answer with JSON, never an HTML page, so an agent can parse
the failure without scraping. The envelope extends the structure above with a stable
machine-readable code and a resolution hint:
/mcp speaks JSON-RPC 2.0 and uses a different error shape. See MCP errors below.
Do not match on error.status there.
{
"error": {
"code": 404,
"status": "NOT_FOUND",
"message": "No API route at /api/unknown.",
"hint": "Available routes: /api/assistant/session, /api/assistant/message, /api/assistant/abort, /api/try.",
"documentation": "https://docs.ixo.world/api-reference/errors"
}
}Envelope fields
| Field | Type | Meaning |
|---|---|---|
code | integer | The HTTP status code, repeated in the body. |
status | string | Stable machine-readable code. Match on this, not on message. |
message | string | What went wrong, in one sentence. |
hint | string | How to resolve it. Present whenever a resolution exists. |
documentation | string | URL of the page describing this class of error. |
Status codes
The status field takes one of these values:
BAD_REQUEST, UNAUTHENTICATED, PERMISSION_DENIED, NOT_FOUND, METHOD_NOT_ALLOWED,
PAYLOAD_TOO_LARGE, RESOURCE_EXHAUSTED, BAD_GATEWAY, UNAVAILABLE.
Rate limits
Every /api/* and /mcp response carries
RFC 9331 rate-limit headers, so an agent can pace
itself from the published quota:
| Header | Example | Sent on | Meaning |
|---|---|---|---|
RateLimit-Policy | "mcp";q=120;w=60 | every response | The quota (q) per window of w seconds. |
RateLimit | "mcp";r=0;t=60 | 429 only | Requests remaining (r) and seconds until the window resets (t). |
Retry-After | 60 | 429 only | Seconds to wait before retrying. |
The quotas are 120 requests per minute per IP on /mcp (policy name mcp) and 10 per minute per
IP on /api/* (policy name api).
RateLimit is sent only on a 429. The edge limiter reports allow or deny, not a live counter, so
a remaining count is published only when it is known to be exactly 0. An invented number would
be worse than none. Pace from RateLimit-Policy.
A 429 on /api/* uses the envelope above with status: "RESOURCE_EXHAUSTED". A 429 on /mcp uses
the same HTTP envelope, because the limiter runs before the JSON-RPC layer.
MCP errors
/mcp implements JSON-RPC 2.0, so protocol failures use the JSON-RPC error object rather than the
envelope above. There is no status, hint or documentation field:
{
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32601, "message": "Method not found: tools/invoke" }
}| JSON-RPC code | Meaning |
|---|---|
-32700 | Parse error. The body was not valid JSON. |
-32600 | Invalid request. Not a JSON-RPC 2.0 envelope. |
-32601 | Method not found. |
-32602 | Invalid params, including an unknown tool name. |
A tool that fails does not produce a JSON-RPC error. It returns a normal result with
isError: true and the reason as text content, which is how MCP reports tool-level failures:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [{ "type": "text", "text": "No page at /nope. Use search_docs to find the right URL." }],
"isError": true
}
}Transport-level failures before the JSON-RPC layer use HTTP status codes with the JSON envelope
documented above. Two examples are a 429 from the rate limiter, and a GET or DELETE on /mcp
when no stream is available.
A path that does not exist returns a real HTTP 404, never a 200 with an app shell. Clients that
do not ask for HTML get a short Markdown body listing the sitemap, llms.txt, the MCP server and
the docs index, so an agent can recover in one more request.
Best practices
| Practice | What to do |
|---|---|
| Use HTTP status codes | Rely on HTTP status codes to understand error types and handle them appropriately. |
| Implement retry logic | For 5xx errors, implement retry logic with exponential backoff. |
| Validate input | Validate all input parameters before sending requests to minimize 400 errors. |
| Respect rate limits | Monitor and respect rate limits to avoid 429 errors. |
Common error solutions
| Error | Solution |
|---|---|
| 400 Bad Request | Double-check request syntax and parameters |
| 401 Unauthorized | Verify access token and re-authenticate if needed |
| 403 Forbidden | Verify user permissions and roles |
| 500 Internal Server Error | Retry after delay or contact support |
Log and monitor errors so you can see patterns and improve your error handling.