Skip to main content

Errors

Every error response, on every route, uses one JSON shape, and every condition the API can report has a stable code. This page is the registry.

The error envelope

Every error response is the OpenAI error envelope:

{
"error": {
"message": "Model 'traffic-bot' was not found. Available models: court-assistant, jury-helpdesk. Omit the 'model' field to use this account's default.",
"type": "invalid_request_error",
"param": "model",
"code": "model_not_found"
}
}
FieldTypeDescription
messagestringHuman-readable explanation. Wording may improve over time; do not branch on it.
typestringThe error class (below).
paramstring or nullThe request field at fault, in bracket form (messages[2].tool_call_id, tools[0], input[3], after), or null when no single field is responsible.
codestring or nullStable, machine-readable code from the registry, or null for validation and server errors.

Program against the HTTP status and code, and use param to point the user at the field to fix. The X-Request-ID response header identifies the exact request; quote it when contacting support (request ids). The official OpenAI SDKs raise typed exceptions for every status and expose code, param, and type on them (openai.NotFoundError, openai.RateLimitError, and so on).

Error codes are an open set: new codes may be added within /v1 (Versioning). Handle an unknown code by falling back on the HTTP status class.

Error types

typeStatusesMeaning
authentication_error401The request did not authenticate
invalid_request_error400, 403, 404, 405, 413The request cannot be served as sent; fix it before retrying
rate_limit_error429A per-account limit or quota is exhausted; retry after Retry-After
server_error500, 503The API or a service it depends on failed; retry with backoff

Handling by status

StatusMeaningHandling
400Malformed or invalid requestFix the field named by param; do not retry unchanged
401Key missing, malformed, expired, or revokedCheck the key (Authentication)
403Key lacks the required scopeUse a key with the scope; do not retry with the same key
404No such model, conversation, document, or job on your account, or an unknown pathCheck the id or the path; re-list models or documents
405The path exists but not for this methodUse a method from the Allow header
413Request body over the capSend less per request
429Rate limited or out of quotaWait Retry-After seconds, then retry with backoff
500Unexpected failureRetry with backoff; then contact support with the request id
503The underlying language model or the text embedding service is unavailableWait Retry-After seconds and retry

Error code registry

One section per code, ordered by status. Validation errors carry code: null and are described first; the 404 and 405 responses for unknown routes are covered at the end.

400: validation errors

Status 400. type invalid_request_error. code null.

The request body failed validation or an endpoint-level check. Only the first failure is reported; param names the field.

{
"error": {
"message": "Input should be 'system', 'developer', 'user', 'assistant' or 'tool'",
"type": "invalid_request_error",
"param": "messages[0].role",
"code": null
}
}

param forms:

paramMeaning
temperature, stream, inputA top-level field
messages[2].tool_call_id, tools[0], input[3], messages[1].tool_calls[0]An item of an array, zero-based, and a field within it
stream_options.include_usage, metadata.notesA nested field, or a key of a map
nullThe body is missing (Field required; POST /v1/conversations instead treats a missing or empty body as {}), is not a JSON object, or is not valid JSON (Invalid JSON in request body., checked before authentication); the request was sent without Content-Type: application/json; or the Content-Length header is not an integer (Invalid Content-Length header., checked on every route before routing and authentication)

Type and bound failures use generic messages such as Field required, Input should be a valid string, Input should be a valid boolean, Input should be a valid integer, got a number with a fractional part, Input should be less than or equal to 2, and List should have at most 200 items after validation, not 201. Values are never coerced from strings (strict typing).

Endpoint checks have specific messages. Chat completions (guide):

paramMessage
nOnly n=1 is supported.
messagesAt least one user message is required.
messagesThe first non-system message must have role 'user'.
messagesThe last message must be a user or tool message.
messages[i].contentMessage content must not be empty.
messages[i].contentMessage content must be at most 100000 characters.
messages[i].content[j].typeA content part whose type is not text (for example image_url): a type error on that part
messages[i].content[j].textText content parts require a text string. (a text part whose text is missing or not a string)
messages[i].tool_call_idA tool message requires tool_call_id.
messages[i].tool_call_idtool_call_id does not match a preceding tool call. (the id is not in the tool_calls of the nearest preceding assistant message, that call was already answered, or no assistant tool call precedes the tool message, including a leading one)
messages[i].tool_calls[j]A malformed assistant tool call in history (id, type, function.name, or function.arguments missing or of the wrong type)
tools[i]The tool's type is not function; function.name is missing or not 1 to 64 letters, digits, underscores, and dashes; function.description is not a string (function.description must be a string.); or function.parameters is not an object (function.parameters must be a JSON Schema object.)
tool_choicetool_choice must be 'auto', 'none', 'required', or {"type": "function", "function": {"name": "<tool name>"}}. (the shape is checked even when no tools are sent, after the message rules and the tool definitions)
response_formatresponse_format.type must be one of 'text', 'json_object', 'json_schema'.; response_format of type 'json_schema' requires json_schema.name (string) and json_schema.schema (object).; response_format (JSON mode) cannot be combined with tools.
stopstop sequences must be non-empty strings.
modelMultiple models are available for this account; pass 'model' explicitly, or ask eCourtDate to set a default model for your account. (model omitted, null, blank, or default while several bots are enabled and no default is configured; see Models)

Embeddings (guide):

paramMessage
inputinput must not be empty. (an empty string or an empty array)
inputinput must be at most 100000 characters. (a single string)
input[i]input strings must not be empty.; input strings must be at most 100000 characters.; or the item is not a string
input[96]input may hold at most 96 strings.
dimensionsThis model produces 1024-dimensional embeddings; the 'dimensions' parameter is not supported. (any value other than a JSON integer is a type error on the same field)

Knowledge base (Ingesting files, Crawling websites, Documents):

paramMessage
fileToo many files: 21 (max 20).; or no file part at all (Field required)
file[i]File exceeds the 26214400 byte limit.
file[i]File type '.exe' is not allowed. Allowed: .csv, .docx, .eml, .htm, .html, .md, .pdf, .txt, .xlsx.
file[i]File content does not match its '.pdf' extension.
file[i]File 'notes.txt' is not valid UTF-8 text.
file[i]Uploaded file is empty.
file[i]Filename exceeds 255 bytes.; Filename contains invalid characters. (a /, \, a control character, or another non-printable character such as a non-breaking space or a zero-width joiner in the name)
namespaceDoes not match ^[a-z0-9][a-z0-9_-]{0,63}$; on POST /v1/ingest/crawl the field is required (Field required); on GET /v1/documents the filter is empty
seed_urlsList should have at least 1 item after validation, not 0; more than 100 items
seed_urls[i]Seed URL '<url>' is not allowed: <reason> (public hosts only)
max_pages, max_depth, rate_limit_rpsOut of range (limits)
limitOut of range on GET /v1/documents (1 to 1000)
afterUnknown document id in after: the cursor is not a document on your account
contentString should have at least 1 character; String should have at most 100000 characters; or whitespace-only content on a conversation message
metadataDictionary should have at most 16 items after validation, not 17 on a conversation; a metadata that is not an object
metadata.<key>A key over 64 characters (String should have at most 64 characters), a value over 512 characters (String should have at most 512 characters), or a non-string value (Input should be a valid string)

One 400 names no field. When the underlying language model or the text embedding service refuses the request as sent, the response is 400 with param: null, code: null, and the message The model provider rejected the request.; nothing was stored or charged. Change the request (typically a tool definition, a JSON schema, or a parameter combination the model cannot accept) before retrying. Inside a stream the same rejection arrives as an in-band frame with type: invalid_request_error (errors inside a stream).

Recovery: fix the field and resend. Nothing was stored or charged.

invalid_api_key

Status 401. type authentication_error. param null. Headers WWW-Authenticate: Bearer.

The request did not authenticate: the Authorization header is missing or malformed, the key is unknown, the secret is wrong, the key has expired or been revoked, or the account is suspended. The body is identical in every case.

{
"error": {
"message": "Incorrect API key provided.",
"type": "authentication_error",
"param": null,
"code": "invalid_api_key"
}
}

Recovery: send Authorization: Bearer ecd_sk_... with the key eCourtDate activated for your account. If a working key starts returning 401, it has expired or been revoked; see Key lifecycle. Do not retry automatically.

insufficient_scope

Status 403. type invalid_request_error. param null.

The key authenticated but lacks the scope the operation requires (chat or ingest). The message names the missing scope. Checked before the rate limiter, so the request does not consume budget.

{
"error": {
"message": "This API key does not have the 'chat' scope.",
"type": "invalid_request_error",
"param": null,
"code": "insufficient_scope"
}
}

Recovery: use a key that carries the scope (scope matrix) or ask eCourtDate to add it. Retrying with the same key will fail again.

tool_not_allowed

Status 400. type invalid_request_error. param tools[i].

The request offered a tool that the bot is not allowed to use. Bots carry an allowlist of tool names configured by eCourtDate; the first offending tool, in request order, is named, and param carries its index. The check runs even when tool_choice is none, and the model is not called.

{
"error": {
"message": "Tool 'get_weather' is not allowed for this model.",
"type": "invalid_request_error",
"param": "tools[1]",
"code": "tool_not_allowed"
}
}

Recovery: remove the tool from tools or ask eCourtDate to add it to the bot's allowlist. See Tools and structured output.

model_not_found

Status 404. type invalid_request_error. param model.

The model value is not an enabled bot slug or alias on your account. A disabled bot, another account's bot, and a misspelled id all produce the same response; the lookup is case-sensitive. The message lists the ids you can use.

{
"error": {
"message": "Model 'Court-Assistant' was not found. Available models: court-assistant, jury-helpdesk. Omit the 'model' field to use this account's default.",
"type": "invalid_request_error",
"param": "model",
"code": "model_not_found"
}
}

The closing sentence of the hint depends on whether omitting model would work: Omit the 'model' field to use this account's default. when a default resolves, or Pass one of them. when several bots are enabled and no default is configured. When no bot is configured yet, the whole hint reads This account has no models configured; omit the 'model' field to use the default assistant.

Returned by POST /v1/chat/completions, GET /v1/models/{modelId} (including GET /v1/models/default when several bots are enabled and no default is configured), POST /v1/conversations, and POST /v1/conversations/{conversationId}/messages (a conversation bound to a bot that was later disabled fails here on its next message).

Recovery: call GET /v1/models and use one of the listed ids, or omit model to use the default. See Models.

not_found

Status 404. type invalid_request_error. param null.

No conversation, document, ingest job, or crawl job with that id exists on your account, or the path itself is unknown. An id that belongs to another account, a deleted resource, and a malformed id all produce this same response, so the API never confirms that an id exists elsewhere.

{
"error": {
"message": "Conversation not found.",
"type": "invalid_request_error",
"param": null,
"code": "not_found"
}
}

Messages: Conversation not found., Document not found., Job not found., Crawl job not found.. Returned by GET/DELETE /v1/conversations/{conversationId}, POST /v1/conversations/{conversationId}/messages, GET/DELETE /v1/documents/{documentId}, GET /v1/ingest/jobs/{jobId}, and GET /v1/ingest/crawl/{crawlJobId}. A request for a path that does not exist returns the same code with the message Unknown route. (unknown routes).

Recovery: check the id against what your own system recorded when the resource was created, or list documents with GET /v1/documents. A second DELETE on an already-deleted resource is 404, not 204.

request_too_large

Status 413. type invalid_request_error. param null.

The request body exceeds the cap: 5,242,880 bytes (5 MiB) on every endpoint except POST /v1/ingest/files, whose cap is 525,336,576 bytes (20 files of 25 MiB plus 1 MiB of multipart overhead). For a body sent with a Content-Length, the check runs before routing and authentication, so it fires even without a key. A body sent without Content-Length (chunked) is capped while it is read, after routing: it is still rejected without a key, but an unknown path (404) or an unsupported method (405) is reported first.

{
"error": {
"message": "Request body exceeds the 5242880 byte limit.",
"type": "invalid_request_error",
"param": null,
"code": "request_too_large"
}
}

Recovery: send less per request. For chat, trim history (only the most recent turns matter) or move it to a conversation; for embeddings, batch fewer inputs; for uploads, send fewer files per request. See Limits.

rate_limit_exceeded

Status 429. type rate_limit_error. param null. Headers Retry-After.

Your account has made more requests this minute than its limit allows, or the underlying language model is itself rate limited. The message tells you which:

MessageRetry-After
Rate limit exceeded: 60 requests per minute.Seconds left in the current minute window
The model is currently rate limited. Please retry shortly.10
Rate limiter unavailable.5 (this response carries no RateLimit-* headers, because nothing was computed)
{
"error": {
"message": "Rate limit exceeded: 60 requests per minute.",
"type": "rate_limit_error",
"param": null,
"code": "rate_limit_exceeded"
}
}

The limit is per account, shared by all keys and all /v1 endpoints, and the RateLimit-* headers on every authenticated response show where you stand (on the per-minute 429, RateLimit-Remaining is 0 and RateLimit-Reset equals Retry-After). Rejected requests count toward the window too, so a tight retry loop makes it worse.

Recovery: wait Retry-After seconds, then retry with exponential backoff. Inside a stream the same error arrives as an in-band frame without a header; treat it as Retry-After: 10.

insufficient_quota

Status 429. type rate_limit_error. param null. Headers Retry-After.

Your account's daily token quota is used up. The quota counts input and output tokens across chat completions, conversation messages, and embeddings, resets at midnight UTC, and, once reached, blocks every /v1 operation, including ones that call no model.

{
"error": {
"message": "Daily token quota exceeded: 200000 tokens per day.",
"type": "rate_limit_error",
"param": null,
"code": "insufficient_quota"
}
}

Retry-After is the number of seconds until the quota resets; the RateLimit-* headers on this response report RateLimit-Remaining: 0 and the same number of seconds in RateLimit-Reset.

Recovery: wait for the reset, or contact eCourtDate to raise the quota for your account. Do not retry on a backoff schedule; the reset time is known. See Rate limits.

upstream_unavailable

Status 503. type server_error. param null. Headers Retry-After: 5.

The underlying language model or the text embedding service timed out or failed. The request was not served, nothing was stored, and no tokens were charged.

{
"error": {
"message": "The model provider is temporarily unavailable. Please retry shortly.",
"type": "server_error",
"param": null,
"code": "upstream_unavailable"
}
}

Recovery: wait Retry-After seconds and retry; the request is safe to resend. If it persists, contact support with the X-Request-ID.

500: server errors

Status 500. type server_error. code null. param null.

An unexpected failure inside the API. The response always carries an X-Request-ID, and the body never includes internal detail.

{
"error": {
"message": "Internal server error.",
"type": "server_error",
"param": null,
"code": null
}
}

The message is always Internal server error.. Inside a stream the same body arrives as an in-band frame.

Recovery: retry once or twice with backoff. A conversation turn that fails this way is not stored, so resending the message is safe. If the error repeats, contact support with the request id.

Unknown routes and methods

A request for a path that does not exist returns 404 with code: not_found; a request for an existing path with an unsupported method returns 405 (code: null) with an Allow header listing every supported method in alphabetical order. Both use the envelope and are evaluated before authentication.

{
"error": {
"message": "Unknown route.",
"type": "invalid_request_error",
"param": null,
"code": "not_found"
}
}
HTTP/1.1 405 Method Not Allowed
Allow: DELETE, GET
Content-Type: application/json
X-Request-ID: 74cdbccc0ffa4bfc95c8cf3823059a02
{
"error": {
"message": "Method not allowed.",
"type": "invalid_request_error",
"param": null,
"code": null
}
}

Common causes: a trailing slash on the path (use the canonical path; a trailing slash redirects with 307), GET on /v1/chat/completions, HEAD on any route, or GET /v1/conversations (there is no list endpoint).

Errors inside a stream

Once a streamed response has started, the HTTP status is already 200. A failure after that point arrives as a frame whose JSON is the error envelope, followed by data: [DONE]:

data: {"error":{"message":"The model is currently rate limited. Please retry shortly.","type":"rate_limit_error","param":null,"code":"rate_limit_exceeded"}}

data: [DONE]

No finish chunk or usage chunk follows, nothing is stored or metered, and there is no Retry-After header. Check every frame for an error key before reading choices.

Branch on the frame's type to decide whether to resend:

typecodeMeaningRetry?
rate_limit_errorrate_limit_exceededThe underlying language model is rate limitedYes, after about 10 seconds
server_errorupstream_unavailableThe model timed out or is unavailableYes, with backoff
server_errornullAn unexpected failure (Internal server error.)Yes, a few times
invalid_request_errornullThe underlying language model rejected the request as sent (The model provider rejected the request.)No; change the request

See Streaming for the frame sequence.

Which errors to retry

CodeRetry?How
rate_limit_exceededYesAfter Retry-After, with backoff and jitter
upstream_unavailableYesAfter Retry-After (5 seconds)
server_error (500)Yes, a few timesExponential backoff
insufficient_quotaAt the resetRetry-After is the seconds until the quota resets
invalid_api_key, insufficient_scopeNoFix the key
model_not_found, not_foundNoFix the id
tool_not_allowed, request_too_large, validationNoFix the request

The official OpenAI SDKs retry 429, 500, and 503 automatically and honor Retry-After; see Rate limits for tuning and for a hand-written loop.