Skip to content
kenari.

Errors

kenari returns errors in the same format as the API you called. Handle an error by its HTTP status and its code, or error.type on Messages, never by the message text. Most messages are in English and a few are in Indonesian, and the wording can change.

Every endpoint except Messages returns this body: Chat completions, Responses, Embeddings, Rerank, Moderations, Images, audio, music, videos, OCR, web and X search, models and account quota.

{
"error": {
"message": "free-model rate limit reached; slow down and retry",
"type": "rate_limit_exceeded",
"param": null,
"code": "free_quota_rpm"
}
}

code is the field to branch on. type repeats code for most errors. The exceptions are the free-model limits (type is rate_limit_exceeded), provider rejections (type is invalid_request_error), shared_key_not_allowed, restricted_key_not_allowed and cli_login_key_required (type is permission_error) and an internal failure on GET /v1/account/balance, GET /v1/account/quota and the Usage endpoints (type is api_error, code is internal_error). param is always null, except on a 400 from the Usage endpoints, where it names the parameter. The Workspace API sets type from the HTTP status instead, such as conflict_error for every 409.

Messages, including count_tokens, returns Anthropic-style errors. There is no code field, so branch on the HTTP status and error.type.

{
"type": "error",
"error": {
"type": "rate_limit_error",
"message": "free-model rate limit reached; slow down and retry"
}
}
error.typeHTTP status
invalid_request_error400, or the provider’s own status for a provider rejection
not_found_error400, or the provider’s own status for a provider rejection
payment_required402
permission_error403
rate_limit_error429
api_error400, 500 and 503

The HTTP status is the same as in the OpenAI format for the same failure. A provider rejection keeps the provider’s own status, so these two types are not always 400. See Provider rejections. api_error arrives with 400 when a provider answered with a client error that kenari did not classify.

The Claude Managed Agents passthrough uses this envelope as well, with a code on the errors kenari raises itself, such as auth_failed. An error from Anthropic passes through unchanged and has no code. Branch on code when it is there and on error.type when it is not.

After a stream has started, the HTTP status is already 200, so a failure arrives as an event in the stream. Check every stream for these events. See Streaming.

API formatHow the error arrives
Chat completionsA data: chunk with an error object whose type is upstream_error
MessagesAn event: error event with the Anthropic body above
ResponsesA response.failed event, with the error in response.error

On Chat completions and Responses, code is stream_interrupted when the provider stopped before the answer was complete. The Messages event has error.type and no code. Send the request again as a new request. A provider rejection in the stream uses the codes under Provider rejections.

Some responses are plain text, not JSON, because the request is rejected before it reaches the API.

StatusBodyCause
400Failed to parse the request body as JSON...On /v1/web/search and /v1/ocr, the body is not valid JSON
401missing authorizationNo key header was sent
401invalid keyThe key does not start with kn-, does not exist, was revoked, has expired, or belongs to an account that is suspended or being deleted
408request body timed outThe body stopped arriving for 30 seconds, or took more than 5 minutes to arrive. Send it again
413request body too largeThe request body is over 32 MiB
415Expected request with Content-Type: application/jsonOn /v1/web/search and /v1/ocr, the Content-Type: application/json header is missing
422Failed to deserialize the JSON body...On /v1/web/search and /v1/ocr, a required field is missing or has the wrong type
429too many requests, try again shortlyOne client IP sent too many requests on a shared key. See Rate limits.
500db errorkenari could not look up the key. Retry.
503server busy, try again shortlykenari is receiving too many large request bodies at once. Retry shortly

The Claude Managed Agents passthrough answers differently. Its 401, its 429 on a shared key and its db error arrive as JSON in the Anthropic format with the code auth_failed. A 500 for any other failure inside kenari is the plain text internal error.

For keys, see Authentication and API keys.

CodeWhenWhat to do
bad_requestThe body or a parameter is invalid, or the key’s restrictions refuse the request (model not allowed, capability not granted, route on a restricted key). An attached document with an unsupported type, a size or page count over the limit, or a length over the model’s context is also refused here. On Embeddings, Rerank, Moderations and System One, a model that is unknown or of another kind is refused here too, and the message lists the ids that are available. Creating a per-tool key is refused here with key limit reached when your account is at its key limitRead message, fix the request.
model_not_foundOn Chat completions, Responses and Messages, the model id or route name does not exist, or you cannot use it. On Embeddings, Rerank, Moderations and System One, the model is listed but no provider serves it right nowPick an id from GET /v1/models, or retry later.
request_blockedkenari refused the request under its content policy before it reached a providerChange the content of the request.
byok_malformed_model_idA byok/ model id is not in the form byok/<provider>/<model>Fix the id. See Providers.
byok_provider_not_foundYou have no key or connection for that providerAdd the key or connect the subscription, or fix the provider name.
invalid_pathA path on the Claude Managed Agents passthrough contains .., an encoded slash or a backslashSend a normal path.
upstream_errorA provider answered with a client error that kenari did not classifyCheck the model and the request fields, or try another model.
invalid_requestA Workspace API request has a body that is not valid JSON for the route, a value that the workspace rules refuse, a malformed id in the path or a malformed Idempotency-Key headerRead message, fix the request.
idempotency_key_requiredA Workspace API route that buys, changes or reactivates seats, or moves Personal balance, has no Idempotency-Key headerSend a unique Idempotency-Key. See Workspace API.
pay_from_personal_not_supportedA Workspace API seat request has a pay_from other than workspaceMove Personal balance into the workspace first, then buy the seats. See Workspace API.

These codes mean the model’s provider rejected the request itself. The HTTP status is 400, or the provider’s own 403, 404, 413, 415, 422 or 429, and type is invalid_request_error.

CodeWhenWhat to do
invalid_request_errorThe provider rejected the request as malformed or unsupportedSend the same request to another model. If it works there, report it to kenari support.
context_length_exceededThe prompt plus max_tokens exceed the model’s context windowShorten the input, lower max_tokens, or use a model with a longer context.
model_not_foundThe provider does not serve the model right nowTry again later or use another model.
content_policy_violationThe provider blocked the request under its content policyChange the content of the request.
prefill_unsupportedThe last message is from the assistant, and the model cannot continue itEnd the conversation with a user or tool message.
reference_image_fetch_failedA video request carried a reference image the provider could not fetchSend each reference as a public https URL or a data URL.
upstream_rejectedThe provider rejected the request for a reason kenari could not classifyCheck the model id and the request fields.

kenari does not retry a rejected request on the same provider. In a route or a BYOK chain it can move on to the next step, because one provider’s rejection does not prove the next one will reject the same request, but a context length rejection ends the chain. The message is written by kenari and never contains the provider’s own text.

The body is plain text. See Plain-text responses. Check the key, or create a new one in the dashboard.

The Claude Managed Agents passthrough answers in JSON instead, with this code:

CodeWhenWhat to do
auth_failedThe kn- key is missing, wrong, expired or revokedCheck the key, or create a new one in the dashboard.
CodeWhenWhat to do
insufficient_balanceYour balance cannot cover the request or the fee of a web search, page fetch or X search, or the owner of a shared API key has run out of fundsTop up, or send a shorter prompt or a lower max_tokens. The message shows your available balance and the most this request could cost, which is the input estimate plus max_tokens at the output rate. A document read with the ocr engine or /v1/ocr has its own message: it says that document reading is billed per page from your balance and not covered by plans, and shows the balance available and the most the document reserves. Top up, or send the file without the ocr engine so that it is read as ordinary input tokens.
payg_limit_reachedYou reached the PAYG balance limit you set in your subscription settings. It covers a rolling day or 30 daysRaise or remove the limit, or wait for the window to roll. Plan quota and free models are not affected.
key_spend_cap_reachedThe API key’s spend cap or token cap is used up. Your balance is not the problemThe key’s owner raises the spend cap or resets its spend on the API Keys page (an unshared key with a spend cap), or uses another key. A token cap and a shared key’s caps cannot be edited, so the owner creates a new key. The owner also gets a notification the first time the cap runs out.
member_spend_cap_reachedYou are a member of a team workspace and you reached the spending limit its Owner or an Admin set for you. The limit counts what you draw from the workspace balance over a rolling 30 days: PAYG requests, use beyond your seat’s quota, web search, web fetch, X search, images and video. Before that point kenari lowers max_tokens to what your remaining limit can pay for, the same way it does for a low balanceAsk the workspace Owner or an Admin to raise or remove your limit on the Team page, or wait for the 30-day window to roll. Your seat’s quota and free models are not affected. You also get a notification the first time you reach the limit in a window.

See How billing works.

The Workspace API has its own 403 codes. They are listed here, and the page explains each one.

CodeWhenWhat to do
subscribers_onlyThe model is only for accounts with an active subscriptionSubscribe, or pick another model. See Subscriptions.
subscription_requiredA byok/chatgpt/... or byok/grok/... request, or a request to the Claude Managed Agents passthrough, from an account without an active planSubscribe. See Providers.
byok_credential_unavailableYou have keys or connections for that provider, but none can serve the request right now because they are disabled, paused or signed outEnable the key, wait for a paused one to recover, or connect the subscription again.
byok_spend_capped_keyA key with a spend cap called a byok/ modelUse a key without a spend cap. A token cap is allowed.
shared_key_not_allowedA shared API key called GET /v1/account/balance, GET /v1/account/quota, a Usage endpoint, a Per-tool keys endpoint, a Workspace API route or the Claude Managed Agents passthroughUse a key from the account itself.
restricted_key_not_allowedA restricted key called GET /v1/account/balance, GET /v1/account/quota, a Per-tool keys endpoint, a Workspace API route or the Claude Managed Agents passthrough, or passed user_id to a Usage endpointUse a key with no model restriction, capability restriction, spend cap or token cap. The key’s own usage is still readable from GET /v1/usage. See Account overview.
cli_login_key_requiredA key other than the one from kenari login called a Per-tool keys endpointUse the key from kenari login. Run kenari login again if your key is older than per-tool keys.
workspace_access_requiredA Workspace API route needs a higher workspace access level than the key has, or the key is one that kenari login saved or a per-tool keyCreate a key with the level the route needs. See Authentication and API keys.
teams_not_enabledA Workspace API route or a kenari_workspace_* MCP tool was called before team workspaces are open for the accountDo not retry. The request succeeds once team workspaces are open for the account.
not_a_team_workspaceA Workspace API route other than GET /v1/workspace was called with a key in a Personal workspaceUse a key from a team workspace.
permission_deniedA Workspace API route for an Owner or Admin was called with a key held by a Member, the workspace rules refuse the change (removing the Owner, for example), or a key held by a Member passed another person’s user_id to a Usage endpointUse a key held by an Owner or Admin, or change the request.
fund_cap_reachedA POST /v1/workspace/fund-from-personal transfer would pass the key’s daily limitWait for the rolling 24 hours to pass, or create a key with a higher limit. The message says how much is left.
CodeWhenWhat to do
not_foundDELETE /v1/account/keys/{id} got an id that is not an active per-tool key of the calling key, or a Workspace API route got a member, invite or seat that is not in the workspaceList your keys with GET /v1/account/keys and use an id from it. See Per-tool keys. For the Workspace API, list the members, invites or seats and use an id from the list.
credential_not_foundThe x-kenari-credential header on the Claude Managed Agents passthrough is not an active Anthropic key of your accountUse the id of an active Anthropic key.
CodeWhenWhat to do
child_key_capThe login key already holds the most per-tool keys it canRevoke one with DELETE /v1/account/keys/{id}, then create the new key. See Per-tool keys.
anthropic_credential_requiredThe Claude Managed Agents passthrough found no active Anthropic API key on your accountAdd an Anthropic key on the Providers tab.
credit_limit_reachedThe Anthropic key named in x-kenari-credential on the passthrough has used up its Claude credit, and auto-stop is onRaise the credit, turn auto-stop off or wait for the renewal day. See Providers.
credential_requiredThe passthrough found two or more active Anthropic keys and no x-kenari-credential headerName one key in the header, or disable the others.
conflictThe state of the workspace refuses a Workspace API change, such as inviting a person who is already a member, a workspace at its member limit, or removing the last seatRead message, and read the state with the GET routes.
idempotency_key_reusedA Workspace API Idempotency-Key was already used for a different body or routeUse a new Idempotency-Key for a new request.
request_in_progressThe first Workspace API request with this Idempotency-Key is still runningRead the state with the GET routes before you retry.

A request body over 32 MiB is refused with a plain-text response, not JSON. See Plain-text responses. A 413 from a model’s provider is a JSON error. See Provider rejections. The code below is for the Claude Managed Agents passthrough, which accepts bodies up to 100 MB.

CodeWhenWhat to do
request_too_largeA request body on the Claude Managed Agents passthrough is over 100 MBSend a smaller body.

Every 429 code is explained, with Retry-After behavior, in Rate limits.

CodeWhen
rate_limit_exceededThe account per-minute limit, the usage reads limit, the per-tool key creation limit, a burst of free requests, a free-model token budget, or music concurrency
free_quota_rpmThe free-model per-minute limit
free_quota_dailyThe free-model daily quota, which resets at 00:00 UTC
plan_limit_reachedA subscription quota window is used up, and PAYG and the free fallback are off
upstream_errorA provider rate limited the request
passthrough_busyToo many Claude Managed Agents passthrough requests are in progress. Try again shortly
rate_limitedThe per-key limits on changes through the Workspace API
pending_limit_reachedA workspace has too many pending invites
daily_limit_reachedA workspace has reached its daily invite limit
CodeWhenWhat to do
internal_errorSomething unexpected failed in kenariRetry once. If it keeps happening, report the request-id header to kenari support.
CodeWhenWhat to do
upstream_errorA provider timed out, could not be reached, or failedRetry with backoff.
all_providers_failedEvery provider, or every step of a route, failed. Retry-After is set when the providers are paused or at capacityRetry after the Retry-After seconds, or use a route with a fallback step. See Routing and routes.
no_wire_for_modalityNo provider currently serves this kind of request for the model, such as chat, image, edit or embeddingTry again later, or use another model.

Error messages never name the provider behind a model. kenari writes every message itself.

A non-streaming request that runs for about two minutes with no response can be cut off by the network edge before the answer reaches you. For long prompts, such as more than 100,000 tokens, large max_tokens, or slow reasoning models, set "stream": true. A stream keeps the connection open while the model works. See Streaming.

When a non-streaming request is still running after a long wait, kenari can keep the connection open by sending spaces before the JSON body. JSON parsers ignore the spaces. The status line is already 200 by then, so check the body for an error object before you read the answer.

  1. On OpenAI-format APIs, use the HTTP status and code. On Messages, use the HTTP status and error.type. Treat message as text for people.
  2. Retry 429 with rate_limit_exceeded, free_quota_rpm or upstream_error, and retry 500 and 503. Wait for Retry-After when it is present, and use exponential backoff with jitter when it is not.
  3. Do not retry other 4xx errors unchanged. Fix the request, the key or the balance first.
  4. For 402, top up or change the limit that was reached, then send the request again.
  5. Check streams for error events, and do not assume every error body is JSON. See Plain-text responses.
  6. Log the request-id response header, and include it when you contact kenari support.