# Errors

Every error has the same shape. Branch on `type` and `code`; `message` is for people and may change.

```json
{
  "error": {
    "type": "invalid_request",
    "code": "invalid_query",
    "message": "The request query are invalid",
    "request_id": "8f14e45f-ceea-4c6d-9a3e-2b7f0c1d9e21",
    "details": [{ "path": "limit", "message": "Too big: expected number to be <=100" }]
  }
}
```

`details` appears on `invalid_request` errors and names each field that failed. Quote `request_id` when you
write to [support@rikor.io](mailto\:support@rikor.io)\: it finds the exact request in our logs.

## Types

| HTTP | `type` | When |
| --- | --- | --- |
| 400 | `invalid_request` | The request is malformed\: a bad parameter, body or cursor |
| 401 | `authentication_error` | No key, or the key is malformed, unknown, revoked or expired |
| 403 | `permission_error` | The key is valid but not allowed to do this |
| 404 | `not_found` | No such record in your scope, or no such endpoint |
| 409 | `invalid_request` | `external_id_taken`\: your id already names another record |
| 429 | `rate_limit_error` | Too many requests; wait for `Retry-After` |
| 500 | `api_error` | Our fault. Retry with backoff; the `request_id` helps us find it |

## Codes

| `code` | Status | Meaning |
| --- | --- | --- |
| `invalid_query` / `invalid_params` / `invalid_body` | 400 | A field failed validation; see `details` |
| `invalid_cursor` | 400 | The cursor is malformed |
| `invalid_range` / `range_too_long` | 400 | A trend range is reversed, or longer than 366 days |
| `body_too_large` | 413 | Request bodies are capped at 64 KB |
| `credential_in_query` | 400 | A key was sent in the URL |
| `internal_header` | 400 | A header reserved for Rikor's own services was sent |
| `missing_key` / `invalid_credential` / `invalid_key` / `unknown_key` | 401 | See [Authentication](/authentication) |
| `key_revoked` / `key_expired` | 401 | The key was revoked, or rolled more than 24 hours ago |
| `insufficient_scope` | 403 | The key lacks the scope this endpoint requires |
| `secret_key_in_browser` | 403 | A secret key was sent from a browser; roll it |
| `ip_not_allowed` / `origin_not_allowed` | 403 | Outside the key's allow-list |
| `partner_only` | 403 | Only partner keys can call this |
| `client_suspended` | 403 | The partner account is suspended |
| `not_found` / `route_not_found` | 404 | No such record in scope / no such endpoint |
| `external_id_taken` | 409 | Your id already names another record |
| `rate_limited` / `too_many_in_flight` | 429 | Per-minute or concurrency limit; see [Rate limits](/conventions/rate-limits) |
| `internal_error` | 500 | Something went wrong on our side |
