IronStratum

Errors

Every error is one envelope — parse it once, handle it everywhere:

{
  "error": {
    "message": "Human-readable description.",
    "type": "invalid_request_error",
    "param": "the_offending_field",
    "code": "invalid_param"
  }
}

param is present only when a specific field caused the error. code is the stable machine handle; type maps onto the OpenAI SDK's error taxonomy. Every response — success or error — carries an x-request-id header: quote it in any support conversation.

The examples below run verbatim against a live stack (export IRONSTRATUM_API_KEY, IRONSTRATUM_REVOKED_KEY, and IRONSTRATUM_BASE_URL first). Codes that are not live yet are documented without an example on purpose — we do not publish curls we cannot stand behind today.

400 invalid_request_error — malformed input

Five codes: invalid_json (unparseable body), invalid_param (a known field with a bad value or an unsupported field), unknown_param (a field we do not accept), context_length_exceeded and sessions_unavailable (session-mode requests — below). Fix the request; no state was created.

An unknown parameter is rejected loudly, never silently ignored:

curl -sS -w '\n%{http_code}\n' $IRONSTRATUM_BASE_URL/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $IRONSTRATUM_API_KEY" \
  -d '{
    "model": "qwen3.8-27b",
    "messages": [{"role": "user", "content": "Hi"}],
    "verbosity": "high",
    "max_tokens": 16
  }'
# expect: 400

context_length_exceeded

The assembled session context — every stored turn plus the new delta — exceeds the model's context_length. Start a new session_id or trim the conversation. Documented without a curl on purpose: overflowing a real session means first filling it past the model's window, which is a slow demo of a deterministic check; the envelope is:

{
  "error": {
    "message": "The assembled session context exceeds this model's context_length. Start a new session_id or trim the conversation.",
    "type": "invalid_request_error",
    "param": "messages",
    "code": "context_length_exceeded"
  }
}

sessions_unavailable

The request carried a session_id, but this gateway runs without the conversation store (JSONL fallback mode). Resend without session_id — full-context requests work unchanged there. Envelope-only for the same reason: the fallback mode is an internal posture, not something a live public stack can demonstrate.

{
  "error": {
    "message": "Conversations are unavailable on this gateway (no conversation store); resend without session_id.",
    "type": "invalid_request_error",
    "param": "session_id",
    "code": "sessions_unavailable"
  }
}

401 authentication_errorinvalid_api_key

The bearer token is missing or unknown. Check the key, or create a new one in the console.

curl -sS -w '\n%{http_code}\n' $IRONSTRATUM_BASE_URL/v1/models \
  -H "Authorization: Bearer sk-ironstratum-not-a-real-key"
# expect: 401

402 insufficient_creditscredit_balance_exhausted

The wallet is empty. The wallet is a prepaid hard cap — it cannot go negative — so a 402 means top-up, never a surprise bill. This code is documented from day one; it fires once the billing wallet ships.

403 permission_errorkey_revoked

The key exists but was revoked. Rotate it out of your configuration:

curl -sS -w '\n%{http_code}\n' $IRONSTRATUM_BASE_URL/v1/models \
  -H "Authorization: Bearer $IRONSTRATUM_REVOKED_KEY"
# expect: 403

404 invalid_request_errormodel_not_found

The model alias does not exist. List live aliases via GET /v1/models:

curl -sS -w '\n%{http_code}\n' $IRONSTRATUM_BASE_URL/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $IRONSTRATUM_API_KEY" \
  -d '{
    "model": "does-not-exist",
    "messages": [{"role": "user", "content": "Hi"}],
    "max_tokens": 16
  }'
# expect: 404

404 invalid_request_errorconversation_not_found

A /v1/conversations id that is missing, deleted, foreign to this key, or malformed — deliberately indistinguishable (never leak which ids exist). List yours via GET /v1/conversations:

curl -sS -w '\n%{http_code}\n' $IRONSTRATUM_BASE_URL/v1/conversations/00000000-0000-4000-8000-000000000000 \
  -H "Authorization: Bearer $IRONSTRATUM_API_KEY"
# expect: 404

Deleting your own conversation — live or already deleted — is an idempotent 204, never this error.

409 invalid_request_erroridempotency_key_reused

An Idempotency-Key was reused with a different request body — a client bug, not a server state. Generate a fresh key per logical operation. First establish a keyed request, then reuse the key with a different body:

curl -sS -w '\n%{http_code}\n' $IRONSTRATUM_BASE_URL/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $IRONSTRATUM_API_KEY" \
  -H "Idempotency-Key: docs-errors-conflict-demo-0001" \
  -d '{
    "model": "qwen3.8-27b",
    "messages": [{"role": "user", "content": "Reply with the word: first"}],
    "max_tokens": 16
  }'
curl -sS -w '\n%{http_code}\n' $IRONSTRATUM_BASE_URL/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $IRONSTRATUM_API_KEY" \
  -H "Idempotency-Key: docs-errors-conflict-demo-0001" \
  -d '{
    "model": "qwen3.8-27b",
    "messages": [{"role": "user", "content": "Reply with the word: second"}],
    "max_tokens": 16
  }'
# expect: 409

429 rate_limit_errorrate_limited

Emitted today in one case: a concurrent request is already holding the same session_id, and the bounded lock wait (30 s by default) expired — back off briefly and retry the same request; nothing was stored. Per-key request/token throttling is still future work; see rate posture and sessions.

500 api_errorinternal_error

Our fault. Retry with an Idempotency-Key if you have one; quote x-request-id if it persists.

502 api_errorupstream_rejected

A provider rejected the request and failover could not rescue it. Retrying with the same body is usually correct — transient provider errors are the common case.

503 api_errorno_provider_available

Every slot in the model's chain is unavailable. Back off and retry, or try another model from GET /v1/models.

SDK mapping

type follows the OpenAI taxonomy: invalid_request_error, authentication_error, insufficient_credits, permission_error, rate_limit_error, api_error — OpenAI SDKs route these to their standard exception classes (AuthenticationError, PermissionDeniedError, …). Handle on code (stable) and display message (human).