> For the complete documentation index, see [llms.txt](https://docs.robinmesh.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.robinmesh.com/api-reference/errors.md).

# Errors

RobinMesh signals failures with conventional HTTP status codes. A failed request also returns a JSON body pairing a stable, machine-readable `code` with a `message` written for humans.

***

## Error response format

```json
{
  "error": {
    "code": "insufficient_credits",
    "message": "Your credit balance (4 credits) is insufficient for the Standard tier (8 credits required).",
    "type": "payment_error",
    "param": null,
    "docs": "https://docs.robinmesh.com/api-reference/errors#insufficient_credits"
  }
}
```

| Field     | Description                                                 |
| --------- | ----------------------------------------------------------- |
| `code`    | Stable identifier meant for program logic                   |
| `message` | Plain-language description of what went wrong               |
| `type`    | Broad category the error falls into                         |
| `param`   | Which request parameter triggered the failure, when one did |
| `docs`    | Deep link into this page, anchored at the matching error    |

***

## HTTP status codes

| Status | Category            | When it occurs                                                                                     |
| ------ | ------------------- | -------------------------------------------------------------------------------------------------- |
| `400`  | Bad request         | The body failed to parse, lacks a required field, or holds an invalid value                        |
| `401`  | Authentication      | The API key is absent or not recognized                                                            |
| `402`  | Payment             | Not enough credits on the wallet                                                                   |
| `403`  | Forbidden           | The key is suspended, or the operation is off limits                                               |
| `404`  | Not found           | No job, model, or webhook matches the identifier                                                   |
| `409`  | Conflict            | The resource already exists or its state conflicts with the operation                              |
| `422`  | Unprocessable       | Well-formed request that fails a semantic rule                                                     |
| `429`  | Too many requests   | You exceeded the per-IP burst limit on the API layer itself; this is unrelated to network capacity |
| `500`  | Server error        | Something failed internally; a retry usually succeeds                                              |
| `503`  | Service unavailable | No worker is hosting the requested model right now                                                 |
| `504`  | Gateway timeout     | The job timed out and your credits came back                                                       |

***

## Error codes

### `invalid_api_key`

The key you sent either never existed or was revoked.

**Status:** 401\
**Action:** Issue a fresh key from Settings.

***

### `missing_api_key`

The request carried no `Authorization` header.

**Status:** 401\
**Action:** Add `Authorization: Bearer rmesh_live_your_key_here` to every request.

***

### `key_suspended`

This key is suspended, which happens when an account gets flagged for abuse.

**Status:** 403\
**Action:** Reach out to <contact@robinmesh.com>.

***

### `insufficient_credits`

The wallet behind this key holds fewer credits than the requested tier costs.

**Status:** 402\
**Action:** Top up at `robinmesh.com/app`, or pick a model from a cheaper tier.

```json
{
  "error": {
    "code": "insufficient_credits",
    "message": "Your credit balance (4 credits) is insufficient for the Standard tier (8 credits required).",
    "type": "payment_error",
    "details": {
      "credits_remaining": 4,
      "credits_required": 8,
      "tier": "standard"
    }
  }
}
```

***

### `model_not_found`

The value in the request's `model` field matches no model ID the network knows about.

**Status:** 400\
**Action:** Fetch `GET /v1/models` and pick an ID from the response.

***

### `no_workers_available`

At this moment, zero workers are hosting the model you asked for. The condition is transient and clears as workers come online.

**Status:** 503\
**Action:** Wait out the `retry_after` interval and try again, or switch models.

```json
{
  "error": {
    "code": "no_workers_available",
    "message": "No workers are currently available for llama-3.3-70b. Try again in approximately 30 seconds.",
    "type": "capacity_error",
    "details": {
      "model": "llama-3.3-70b",
      "retry_after": 30
    }
  }
}
```

***

### `job_timeout`

The 120-second window closed before any worker finished the job. The escrowed credits were returned automatically.

**Status:** 504\
**Action:** Send the request again; the refunded credits are usable right away. Repeated timeouts on one model suggest its worker pool is thin.

```json
{
  "error": {
    "code": "job_timeout",
    "message": "The inference job timed out after 120 seconds. Your credits have been refunded.",
    "type": "timeout_error",
    "details": {
      "job_id": "job_8fx2kp3m9qrstvwxyz",
      "credits_refunded": 8,
      "refund_tx": "0x5e08c3b71a94f2d6580b1ce9f43a07d218e6c5049fb3a827d15e90cb46281f7a"
    }
  }
}
```

***

### `invalid_request`

The body could not be parsed, or a required field is absent.

**Status:** 400\
**Action:** The `param` field names the offending field; start there.

***

### `context_length_exceeded`

Taken together, the messages in the request exceed what the chosen model's context window can hold.

**Status:** 400\
**Action:** Trim the conversation, or move to a model with more context. The `context_window` field in `GET /v1/models` shows each model's limit.

***

### `dispute_window_expired`

The dispute arrived after the 60-second window had already closed.

**Status:** 422\
**Action:** None available. A dispute must be filed within 60 seconds of the final output token, and the window cannot be extended.

***

## Retrying safely

Retry freely on any 5xx and on `no_workers_available` (503). Everything else points to a flaw in the request itself, and resending the same payload will fail the same way.

Recommended retry strategy:

```python
import time
import httpx

def call_with_retry(client, payload, max_retries=3):
    for attempt in range(max_retries):
        response = client.post("/v1/chat/completions", json=payload)
        
        if response.status_code == 200:
            return response
        
        error = response.json().get("error", {})
        code = error.get("code")
        
        if response.status_code == 503 and code == "no_workers_available":
            retry_after = error.get("details", {}).get("retry_after", 10)
            time.sleep(retry_after)
            continue
        
        if response.status_code >= 500:
            time.sleep(2 ** attempt)
            continue
        
        # Do not retry 4xx responses; surface them instead
        response.raise_for_status()
    
    raise Exception(f"Request failed after {max_retries} attempts")
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.robinmesh.com/api-reference/errors.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
