Get Started

Errors & status codes

Every upstream provider error is normalised into a single predictable JSON envelope. Client code never has to handle Vertex's google.rpc.Status alongside Anthropic's error.type alongside OpenAI's error.code.

Error envelope

Every non-2xx response carries this shape. The OpenAI-compatible fields (message, type, code, param) are present on every error; the Meridian extensions (retryable, upstream_provider, upstream_status, provider_attempts) are present when applicable.

JSON
{
  "error": {
    "message": "Upstream provider rate limit hit",
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "param": null,
    "retryable": true,
    "upstream_provider": "anthropic",
    "upstream_status": 429
  }
}

Always read error.retryable instead of inferring retryability from the status code. The router takes provider-specific knowledge into account (for example OpenAI's insufficient_quota is a 429 but is not retryable — that distinction is encoded in retryable: false).

HTTP status codes

  • 200

    OK — Request succeeded. Inspect the billing and X-Meridian-* headers for routing details.

  • 202

    Accepted — Streaming response started; chunks follow as Server-Sent Events.

  • 400

    Bad Request — Malformed body, unknown model, missing messages, or a high-risk request missing purpose / user_consent_id.

  • 401

    Unauthorized — Missing or malformed API key header.

  • 402

    Payment Required — Per-key credit limit reached, OR the user balance has hit the maximum debt threshold.

  • 403

    Forbidden — Key revoked, expired, or the request was blocked by deployer policy / Article 5 detector. Inspect error.code.

  • 404

    Not Found — The user account associated with this key was not found.

  • 409

    Conflict — The requested model is in maintenance or deprecated lifecycle status.

  • 422

    Unprocessable Entity — Capability mismatch (e.g. you sent an image to a text-only model).

  • 429

    Too Many Requests — Per-key rate limit, per-tenant rate limit, or daily multimodal cap exceeded.

  • 502

    Bad Gateway — All providers in the fallback chain failed. Response includes the full provider_attempts array.

  • 500

    Internal Server Error — Unexpected. Always retryable.

Meridian error codes

The error.code field uses these stable string identifiers. The set is closed at the gateway — every upstream code is mapped to one of these so client switch statements stay finite.

CodeMeaning
invalid_api_keyMissing or revoked key.
key_expiredKey passed its expiresAt timestamp.
key_credit_limit_exceededPer-key credit cap hit. The response body includes consumed, limit, and resetInterval.
insufficient_quotaUser balance has reached the maximum debt threshold; top up to continue.
user_not_foundThe key resolved but its owning user is missing — usually a deleted account.
rate_limit_exceededRate limit hit. retryable: true.
authentication_errorAuth header rejected at upstream provider.
permission_deniedAuthorisation rejected at upstream provider.
model_not_foundThe requested model is not in the catalogue, or the upstream provider returned 404.
request_timeoutUpstream took longer than the configured timeout. retryable: true.
invalid_request422 from upstream. Usually a bad parameter combination.
context_length_exceededPrompt exceeded the model's context window. Set auto_truncate: true to drop old messages automatically.
upstream_unavailableUpstream returned 5xx. retryable: true.
all_providers_failedEvery entry in the fallback chain failed. Inspect provider_attempts.
provider_errorUpstream error that didn't match any known shape. Inspect upstream_status.

Provider attempts

When a fallback chain fails, the response includes a provider_attempts array with one entry per attempted provider. Useful for debugging which step in the chain blew up and why.

JSON
{
  "error": {
    "message": "All providers failed",
    "type": "upstream_error",
    "code": "all_providers_failed",
    "retryable": false,
    "provider_attempts": [
      {
        "provider": "openai",
        "model": "gpt-4o",
        "status": "failed",
        "latency_ms": 312,
        "error": "[503] upstream_unavailable"
      },
      {
        "provider": "anthropic",
        "model": "claude-3-5-sonnet",
        "status": "failed",
        "latency_ms": 204,
        "error": "[529] overloaded_error"
      }
    ]
  }
}

Retry strategy

Most retries happen inside the gateway and never reach your client (see Smart retries and Fallback chains). When a retry-eligible error does reach your client, follow this rule:

  • If error.retryable is true, retry with jittered exponential backoff (start at 250 ms).
  • If error.retryable is false, do not retry — it's a deliberate refusal.
  • Never retry 402 key_credit_limit_exceeded or 402 insufficient_quota — top up first.