Errors

Errors

When the gateway refuses a request (the proxy, the OpenTelemetry receiver at /v1/traces, or batch event ingest), the body has one shape:

{
  "error": "Invalid API key",
  "code": "UNAUTHORIZED",
  "hint": "No active key matches this value. ...",
  "docs": "https://docs.metrxbot.com/quickstart"
}
  • error is the human-readable message (kept for clients that already match on it).
  • code is the stable value to branch on.
  • hint names the fix.
  • docs links the page that explains it.

Some codes carry extra fields alongside, listed below.

Authentication

MISSING_API_KEY

401. The Authorization header is missing or does not start with Bearer . Send your Metrx API key as Authorization: Bearer <key>. For an OpenTelemetry exporter, set OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20<key>", with the space percent-encoded.

INVALID_API_KEY_FORMAT

401. The value after Bearer is too short to be a Metrx API key. Copy the full key (it starts with sk_live_) from Settings → Security → API Keys.

UNAUTHORIZED

401. No active key matches the value sent. Check the key was copied in full, has not been revoked, and belongs to this environment; or create a new one in Settings → Security → API Keys.

ORG_INACTIVE

403. The organization that owns the key is deactivated. An org owner can reactivate it, or contact support.

INSUFFICIENT_SCOPE

403. The key is valid but lacks the scope this request needs. GET, HEAD and OPTIONS need read; every other method needs write, including proxied LLM calls, because they are recorded. The body carries required_scope and granted_scopes. Use a key with that scope. An org owner or admin can create one in Settings → Security → API Keys.

SCOPE_CHECK_UNAVAILABLE

503. The gateway could not read this key’s scopes, so it refused the request rather than guess. Retry after the Retry-After interval. If it persists, contact support.

Limits

PAYMENT_REQUIRED

402. The plan has used its monthly call limit. Upgrade at the upgrade_url in the body, or wait for metrx_usage_summary.reset_at. The body also carries current_tier, limit_hit and upgrade_options.

RATE_LIMITED

429. More requests this minute than the plan allows. Wait for the number of seconds in the Retry-After header (also retry_after in the body), or batch events.

BUDGET_EXCEEDED

429. A budget with hard_block enforcement has reached its limit. Raise the limit with Edit limit at Settings → Budgets (then Resume the budget there if it is paused), or wait for the period to reset. The body carries budget_id, spent, limit and enforcement. If you cannot change budgets, ask your admin.

Requests

NOT_FOUND

404. No gateway route matches the method and path. The body’s available_routes lists the routes. A common cause is an OpenAI client whose base URL lacks /v1: use https://gateway.metrxbot.com/v1.

PAYLOAD_TOO_LARGE

413. The body is over the size limit: 10MB for proxied requests, 1MB for OpenTelemetry. For OpenTelemetry, lower the exporter batch size: OTEL_BSP_MAX_EXPORT_BATCH_SIZE=100.

INVALID_JSON

400. The body is not valid JSON. The hint depends on the endpoint:

  • Proxy: the body must be a JSON provider request, e.g. a chat/completions payload.
  • OpenTelemetry: send an OTLP ExportTraceServiceRequest as JSON (OTEL_EXPORTER_OTLP_PROTOCOL=http/json).
  • Batch ingest: send a JSON body of the form {"events": [...]}.

UNSUPPORTED_MODEL

400. The gateway could not map the model to a provider. Send an X-Provider header, or GET /v1/models for the supported list. The body carries model and supported_providers.

MISSING_PROVIDER_KEY

400. A proxied request has no X-Provider-Key header. Pass your LLM provider’s API key in X-Provider-Key. Your Metrx key stays in Authorization.

OpenTelemetry

OTLP_PROTOBUF_UNSUPPORTED

415. The receiver accepts OTLP/HTTP JSON only, and the request was protobuf (or another content type). gRPC is not supported. Set OTEL_EXPORTER_OTLP_PROTOCOL=http/json (or OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/json). Exporters that cannot send JSON, such as Python’s, go through a Collector: see OpenTelemetry.

Batch event ingest

INVALID_EVENTS

400. The body has no events array. Send an events array of LLM events; model and latency_ms are required on each.

BATCH_TOO_LARGE

400. More than 100 events in one request. Split the batch into requests of at most 100 events. The body carries received, the number sent.

Batch event warnings

A 200 from batch ingest can carry warnings, one { index, code } per event that was accepted with a field dropped. The event is stored; only that field is lost. The TypeScript exporter logs each warning.

task_id_not_uuid

The event’s task_id is a string but not a UUID, so it was stored without one and its outcome cannot join to it. Send a UUID. The SDKs’ task() helpers map any string id to a UUIDv5 for you; to do it yourself, use namespace 522984cb-f1f3-5643-933b-8ba17bed1467.

task_id_not_string

The event’s task_id is not a string (for example a number or an object), so it was stored without one. Send the task id as a UUID string.