Errors

On the OpenAI-compatible surface (https://api.recovea.ai/v1), Recovea returns errors in the exact OpenAI shape, so your SDK raises its normal exceptions and your existing error handling keeps working. Every response, error or not, also carries a x-recovea-trace-id header (alongside x-request-id) you can quote to support.

On the Anthropic-compatible surface (https://api.recovea.ai/anthropic), errors use Anthropic's own error envelope instead — {"type":"error","error":{"type","message"},"request_id":"…"}. The two wire formats never mix. This page documents the /v1 (OpenAI) surface.

The error envelope

Every error (any 4xx or 5xx) returns the same nested envelope. All four keys are always present; param and code are JSON null when they don't apply. Recovea never returns a bare string, a flat {message}, or a non-OpenAI shape: SDK exception parsing depends on this structure.

{
  "error": {
    "message": "…",
    "type": "…",
    "param": null,
    "code": null
  }
}
KeyMeaning
messageHuman-readable description. Safe to log, not to branch on.
typeCategory: invalid_request_error, rate_limit_error, permission_error, or server_error. (Like OpenAI, the auth 401 uses invalid_request_error, not a separate authentication_error.)
paramThe offending field path (e.g. "messages[1].content") or null.
codeStable machine string (e.g. invalid_api_key, insufficient_quota, model_not_found) or null. Branch on this.

Status codes

The HTTP status drives SDK retry behavior. Errors Recovea mints itself use the codes below; provider errors are mirrored through verbatim (see Retries), so you may also see other OpenAI status/code pairs (e.g. a 400 with param set, or a 404) exactly as your provider sent them.

StatusMeaningtype / code
401Missing key (code null) or wrong/revoked keyinvalid_request_error / invalid_api_key
403rcv_test_ key with no sandbox upstream configuredpermission_error / test_mode_unavailable
403No provider key connected for this workspacepermission_error / no_provider_key
404Unknown model on /v1/models/{id}invalid_request_error / model_not_found
404Bare non-OpenAI model id on /v1 inference — the message names the vendor/model id to useinvalid_request_error / model_not_found
429Throttle: too many requestsrate_limit_error / rate_limit_exceeded
429Quota exhausted: monthly spend cap reachedrate_limit_error / insufficient_quota
500Stored provider key unusableserver_error / provider_key_unusable
502Upstream provider unreachableserver_error / service_unavailable
503Auth or provider-key lookup temporarily downserver_error / service_unavailable

Two flavors of 429

Throttling and quota exhaustion both return 429, distinguished only by code, not by status:

  • rate_limit_exceeded: you are sending faster than your request/token rate limit. Back off and retry; the Retry-After header tells you how long.
  • insufficient_quota: your monthly provider spend cap has been reached. Retrying will not help; raise or top up your budget.

Branch on error.code, never on the status alone.

Retries

OpenAI SDKs auto-retry 408, 409, 429, and ≥500 with exponential backoff; they do not retry 400, 401, 403, or 404. On a 429, Recovea sends a Retry-After (and retry-after-ms) header so the SDK waits the real reset window instead of guessing.

Two guarantees on the proxy:

  • We never wrap a provider error in a Recovea 500. When the upstream provider returns an error, we mirror its status and envelope verbatim.
  • We never swallow a 429 into a 200 or a 500. Doing so would defeat the SDK's Retry-After-aware backoff and either hammer or stall your client.

Because of fail-open, a failure in Recovea's own optimization layer does not surface as an error: the request falls through to your provider on your own key. The errors here are the provider's or genuine request problems, passed through, not invented.

Example: a 401

An incorrect or revoked key returns 401 with a friendly, OpenAI-shaped body that points new callers to the docs:

{
  "error": {
    "message": "Incorrect Recovea API key provided. View and manage your keys at https://dashboard.recovea.ai. New to Recovea? It's an OpenAI-compatible API to see, govern, and prove your AI spend across every provider — get started at https://recovea.ai/docs.",
    "type": "invalid_request_error",
    "param": null,
    "code": "invalid_api_key"
  }
}

When no key is sent at all, the status is still 401 and type is invalid_request_error, but code is null (a missing key, not a wrong one) — branch on code accordingly.

Catching errors in Python

The OpenAI SDK maps each status to a typed exception, so you catch Recovea errors exactly as you would catch OpenAI's:

import openai
from openai import OpenAI

client = OpenAI(
    base_url="https://api.recovea.ai/v1",
    api_key="rcv_live_…",
)

try:
    resp = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Hello from Recovea"}],
    )
except openai.AuthenticationError as e:        # 401
    print("bad key:", e.code)                  # "invalid_api_key"
except openai.RateLimitError as e:             # 429 (both flavors)
    if e.code == "insufficient_quota":
        print("out of credit: top up")
    else:
        print("throttled: the SDK will back off and retry")
except openai.NotFoundError as e:              # 404
    print("unknown model:", e.code)            # "model_not_found"
except openai.APIStatusError as e:             # any other 4xx/5xx
    print(e.status_code, e.code, e.message)
    print("trace id:", e.response.headers.get("x-recovea-trace-id"))

Every exception exposes e.status_code, the parsed e.type / e.code / e.param, and the response headers, including x-request-id and x-recovea-trace-id (both set by Recovea on every response). Quote the trace id when you contact support.