> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rimp.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Stripe-style error envelope, retry semantics, and full code reference.

All errors return a consistent JSON envelope and include an `X-Request-Id` header so you can quote it to support.

```json theme={null}
{
  "error": {
    "type": "insufficient_credits",
    "code": "insufficient_credits",
    "message": "Wallet balance (120) below requested reservation (240).",
    "request_id": "req_abc123"
  }
}
```

## Error types

| Type                     | HTTP | Retry?                  | Meaning                                                            |
| ------------------------ | ---- | ----------------------- | ------------------------------------------------------------------ |
| `invalid_request`        | 400  | No                      | Body or query failed schema validation. Fix and retry.             |
| `authentication`         | 401  | No                      | Missing or invalid bearer token.                                   |
| `authorization`          | 403  | No                      | Token doesn't have the scope or IP isn't allowed.                  |
| `not_found`              | 404  | No                      | Resource doesn't exist or belongs to another org.                  |
| `conflict`               | 409  | Conditional             | Idempotency-Key reused with different body, or job not cancelable. |
| `insufficient_credits`   | 402  | After top-up            | Wallet below reservation.                                          |
| `rate_limited`           | 429  | Yes, with `Retry-After` | Per-key RPM exceeded.                                              |
| `content_policy_blocked` | 422  | No                      | Prompt or output blocked by moderation.                            |
| `provider_unavailable`   | 502  | Yes (backoff)           | Upstream model is down. Try a different model or wait.             |
| `quota_exceeded`         | 429  | After top-up            | Monthly hard cap reached.                                          |
| `internal`               | 500  | Yes (backoff)           | Bug on our side. Include `request_id` when reporting.              |

## Common error codes

```text theme={null}
invalid_body              — zod validation failed; check `param` field
empty_body                — POST with no JSON body
not_signed_in             — endpoint requires session cookie
not_cancelable            — generation is already succeeded/failed/canceled
idempotency_key_too_long  — keys must be ≤ 200 chars
idempotency_key_reuse     — same key + different body in 24h window
anomaly_paused            — org temporarily blocked by spending anomaly detector
```

## Retry strategy

For `provider_unavailable` and `internal`, retry with exponential backoff:

```ts theme={null}
async function withRetry<T>(fn: () => Promise<T>, attempts = 4): Promise<T> {
  for (let i = 0; i < attempts; i++) {
    try {
      return await fn();
    } catch (err) {
      if (err.status === 429 || err.status === 502 || err.status === 500) {
        const ms = Math.min(30_000, 1_000 * 2 ** i + Math.random() * 500);
        await new Promise((r) => setTimeout(r, ms));
        continue;
      }
      throw err;
    }
  }
  throw new Error('Exhausted retries');
}
```

For `rate_limited` (429), honor the `Retry-After` header instead of exponential backoff.

## Idempotency-Key replay

If a POST creates a generation and the connection drops, **retry with the same `Idempotency-Key`**. The original response is replayed (we send `Idempotent-Replayed: true` in the headers). No duplicate charge.
