Errors
How OpenToken reports errors, the status codes you can expect, and how to handle them.
OpenToken returns errors in the OpenAI-compatible envelope. Every failed request — and any error that surfaces after a streaming response has started — uses the same shape, so a single handler covers them all.
{
"error": {
"message": "Human-readable explanation of what went wrong.",
"type": "invalid_request_error",
"code": "model_not_found"
}
}The HTTP status code, the type, and the more specific code together tell you what happened and whether a retry makes sense.
Status codes
| Status | type | code | Meaning |
|---|---|---|---|
| 400 | invalid_request_error | model_not_found | The request was malformed, or the {provider}/{model} id is not registered. |
| 401 | authentication_error | missing_auth / invalid_key / expired_key | Missing or invalid API key. DB-issued keys start with sk-optk-; environment/demo keys may use other formats. |
| 402 | invalid_request_error | insufficient_credit | The workspace credit balance is exhausted (or no billing account is provisioned). Top up to continue. |
| 429 | invalid_request_error | spend_limit_exceeded | A per-key spend limit was reached. (OpenToken has no general request-rate limiter.) |
| 500 | api_error | — | An unexpected error inside the gateway. |
| 503 | upstream_error | no_supplier | The provider returned an error, or no provider was available to serve the model. |
| 504 | upstream_error | upstream_timeout | The upstream provider did not respond within the gateway deadline. |
Example: unknown model
OpenToken registers google/* (gemini-2.5-pro, gemini-3-flash, gemini-3.1-pro, gemini-3.1-flash-lite, text-embedding-004) and anthropic/* (claude-opus-4-7, claude-opus-4-6, claude-sonnet-4-6, claude-sonnet-4-5, claude-haiku-4-5). Any unregistered id — including any openai/* id — returns a 400 with model_not_found. See Models for the live list.
{
"error": {
"message": "model \"openai/gpt-4o\" not found",
"type": "invalid_request_error",
"code": "model_not_found"
}
}See Models for the full list of registered ids.
Handling errors
Check the HTTP status first, then branch on code for the cases you want to treat specially.
from openai import OpenAI, APIStatusError
client = OpenAI(base_url="https://api.opentoken.kr/v1", api_key="sk-optk-...")
try:
resp = client.chat.completions.create(
model="google/gemini-3-flash",
messages=[{"role": "user", "content": "Hello"}],
)
except APIStatusError as e:
err = e.response.json()["error"]
print(e.status_code, err["type"], err.get("code"), err["message"])import OpenAI, { APIError } from "openai";
const client = new OpenAI({
baseURL: "https://api.opentoken.kr/v1",
apiKey: process.env.OPENTOKEN_API_KEY,
});
try {
await client.chat.completions.create({
model: "google/gemini-3-flash",
messages: [{ role: "user", content: "Hello" }],
});
} catch (e) {
if (e instanceof APIError) {
console.log(e.status, e.type, e.code, e.message);
}
}Errors during streaming
When stream: true, the HTTP status can be 200 before anything goes wrong upstream. An error that occurs mid-stream arrives as a final SSE data event carrying the same { error: { message, type, code } } envelope (with type usually upstream_error and code stream_error or the upstream code). The stream is then terminated with data: [DONE] as usual, so detect the error by inspecting each event's payload for an error field rather than relying on [DONE]. Always handle errors on the event stream, not just on the initial response.