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

# Errors

Every Millimetric error is a JSON response with a stable string `error` field and an HTTP status code. The string is the contract — the human-readable message is *not*.

```json
{
  "error": "invalid_payload",
  "details": { "fieldErrors": { "event": ["Required"] } }
}
```

## Auth errors (401 / 403)

| Status | `error`                      | Trigger                                                                 | Fix                                                                             |
| ------ | ---------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| 401    | `missing_bearer_token`       | No `Authorization` header.                                              | Send `Authorization: Bearer {key}`.                                             |
| 401    | `malformed_api_key`          | Key doesn't match \`(pk                                                 | sk                                                                              |
| 401    | `invalid_api_key`            | No matching key in Supabase, or HMAC doesn't verify.                    | Mint a new key. Old one was revoked or never existed.                           |
| 401    | `key_kind_mismatch`          | Stored key has a different kind than the prefix claims.                 | Re-mint. Likely a copy-paste from another row.                                  |
| 401    | `invalid_session`            | Admin endpoint: user JWT failed Supabase validation.                    | Sign in again.                                                                  |
| 403    | `origin_not_allowed`         | `pk_*` key from an origin not in the project's `allowed_origins`.       | Add the origin in the dashboard, or use `sk_*` server-side.                     |
| 403    | `insufficient_scope`         | Read endpoint called with `pk_*`/`sk_*`, or write endpoint with `rk_*`. | Use a key with the right scope: `pk_/sk_` for ingest, `rk_` for read.           |
| 403    | `forget_requires_secret_key` | `/v1/forget` called with `pk_*`.                                        | Use `sk_*`. Browser keys are explicitly rejected to prevent leak-induced wipes. |

## Validation errors (400)

| Status | `error`            | Trigger                                                                             | Fix                                                                                            |
| ------ | ------------------ | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| 400    | `invalid_payload`  | Body failed Zod parse. `details.fieldErrors` is the flattened tree.                 | Inspect `details`. Common causes: `event` missing, `properties` > 8 KB, malformed `url`.       |
| 400    | `invalid_params`   | Query string failed validation.                                                     | Check `from`/`to` are ISO-8601, `metric` is one of `count`/`uniques`.                          |
| 400    | `invalid_group_by` | Unknown column passed to `/v1/stats?group_by=`. Response includes the allowed list. | Use only: `event_name`, `source`, `medium`, `country`, `device_type`, `browser`, `os`, `path`. |

## Rate limits (429)

| Status | `error`        | Trigger                                                                              | Fix                                                                                          |
| ------ | -------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| 429    | `rate_limited` | Token bucket exhausted for this `(project_id, route)`. `Retry-After` header present. | Back off `Retry-After` seconds, then retry. For sustained throughput, switch to `/v1/batch`. |

Limits:

| Endpoint         | Refill | Burst |
| ---------------- | ------ | ----- |
| `POST /v1/track` | 50/sec | 200   |
| `POST /v1/batch` | 5/sec  | 20    |

(Per-Worker-instance today. Move to a Durable Object if needed.)

## Server errors (5xx)

| Status | `error`           | Trigger                                          | Fix                                                           |
| ------ | ----------------- | ------------------------------------------------ | ------------------------------------------------------------- |
| 500    | `internal_error`  | Unhandled exception. Worker logs have the trace. | Retry once with jitter. If persistent, check `wrangler tail`. |
| 500    | `forget_failed`   | ClickHouse rejected the `ALTER TABLE … DELETE`.  | Worker logs. Likely a transient ClickHouse issue.             |
| 502    | `upstream_failed` | Couldn't reach ClickHouse / Supabase.            | Same as above — retry.                                        |

## MCP-specific errors (JSON-RPC)

The MCP transport uses JSON-RPC error envelopes:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "insufficient_scope",
    "data": { "required": "ingest", "got": "read" }
  }
}
```

| Code   | Meaning                                                 |
| ------ | ------------------------------------------------------- |
| -32600 | `invalid_request` — body not a valid JSON-RPC envelope. |
| -32601 | `method_not_found` — unknown method.                    |
| -32602 | `unknown_tool` / `unknown_resource` / `unhandled_tool`. |
| -32000 | `tool_failed` (server error) / `insufficient_scope`.    |

## Retry guidance

| Error class                           | Retry? | How?                                                                  |
| ------------------------------------- | ------ | --------------------------------------------------------------------- |
| 4xx (validation, auth, scope, origin) | **No** | The payload is wrong. Fix it.                                         |
| 429 `rate_limited`                    | Yes    | Honour `Retry-After`. Use `/v1/batch` for sustained writes.           |
| 5xx                                   | Yes    | Exponential backoff. The Node SDK does 100/200/400/800 ms by default. |
| Network errors                        | Yes    | Same as 5xx.                                                          |

## Worker logs

The HTTP response only ever returns a short `error` code. To see the full traceback:

```bash
pnpm --filter @millimetric/api wrangler tail
# or, in local dev:
pnpm dev:api
```

Every error logs the request path, the project (when known), and the failure point.

## See also

* [API overview](/api-reference/overview.md) — base URL, content type, idempotency.
* [Rate limits](/reference/rate-limits.md) — full table.
* [API keys](/core-concepts/api-keys.md) — key kinds and scopes.


---

# 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:

```
GET https://docs.millimetric.ai/reference/errors.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
