Error codes
Errors are returned via HTTP status codes, machine-readable detail.error codes, and documented response shapes.
Status codes#
| Code | Name | Description |
|---|---|---|
| 400 | Bad Request | Invalid request. Unsupported model ID (with similar-model suggestions), model-specific parameter constraint violations (duration, resolution, reference image count, etc.), or a model outside the key's allowed set. |
| 401 | Unauthorized | Missing, invalid, or expired API key, or a deleted account. |
| 402 | Payment Required | Insufficient credits or exceeded budget/quota limits. The fix differs per code (see the machine-readable codes table below). |
| 403 | Forbidden | Access denied. Key IP allowlist violation, read-only scope, or missing cross-border training consent. |
| 404 | Not Found | Resource not found. Unsupported model ID (single-model lookup), or a nonexistent usage log / job ID. |
| 409 | Conflict | A request with the same Idempotency-Key is already being processed. |
| 413 | Payload Too Large | Request body exceeds the limit (50MB by default). |
| 415 | Unsupported Media Type | Unsupported image or mask MIME type. |
| 422 | Unprocessable Entity | Request schema validation failure (field type or range violations). detail contains an array of locations and reasons. |
| 429 | Too Many Requests | Rate limit exceeded (60 RPM per key by default) or all upstreams rate-limited. Respect the Retry-After header. |
| 502 | Bad Gateway | Upstream provider error. For text requests this means automatic retries and fallback were exhausted. Use the retryable field to decide whether retrying helps. |
| 503 | Service Unavailable | Temporarily unavailable (catalog snapshot unverifiable, limiter failure). Retry after a moment. |
Response shapes#
Error bodies come in three shapes. Most carry a string or object inside detail; request-size and format errors use the OpenAI-compatible { error: { message, type } } shape. Schema validation failures (422) put an array in detail.
{
"detail": {
"error": "insufficient_credits",
"message": "사용 가능 크레딧 부족: 12,000 크레딧 < 54,000 크레딧. 대시보드 → 결제에서 충전해주세요.",
"action": { "type": "topup", "path": "/billing" }
}
}{
"detail": {
"error": "upstream_request_rejected",
"message": "모델 제공자가 요청을 거부했습니다. 잠시 후 다시 시도하거나 다른 모델을 사용해주세요. (upstream 400: ...)",
"retryable": false,
"upstream": { "status": 400, "detail": "..." }
}
}{
"error": {
"message": "요청 본문이 너무 큽니다(최대 50MB).",
"type": "payload_too_large"
}
}{
"detail": [
{
"loc": ["body", "duration_seconds"],
"msg": "Input should be between 2 and 20",
"type": "greater_than_equal"
}
]
}Structured errors may include an action field with a dashboard path, plus retryable, providers, and upstream where applicable. message is always Korean — treat it as human guidance, not a parsing target.
Machine-readable codes#
| Code | Location · HTTP | Meaning and fix |
|---|---|---|
| insufficient_credits | detail.error · 402 | Insufficient credits. Top up in Dashboard → Billing, then retry. |
| monthly_budget_exceeded | detail.error · 402 | Key monthly budget exceeded. Adjust the budget in key settings, not by topping up. |
| monthly_token_quota_exceeded | detail.error · 402 | Key monthly token quota exceeded. Adjust the quota in key settings. |
| org_member_monthly_budget_exceeded | detail.error · 402 | Team member monthly budget exceeded. Ask a team admin to adjust it. |
| xborder_training_consent_required | detail.error · 403 | Missing separate consent for training-use providers. Returned with a providers list; consent in Settings resolves it. |
| org_membership_required | detail.error · 403 | A valid team membership is required. |
| upstream_rate_limited | detail.error · 429 | All upstreams rate-limited. Retry after Retry-After. |
| upstream_unavailable | detail.error · 502 | Transient upstream failure (retryable=true). Retry after a moment. |
| upstream_request_rejected | detail.error · 502 | Upstream rejected the request (retryable=false). Fix the request or switch models. The upstream field carries a summary. |
| catalog_snapshot_stale | detail.error · 503 | Catalog snapshot unverifiable. Retry after a moment. |
| upstream_error | SSE error.code | Upstream dropped mid-stream (SSE). partial holds what was received; only that part is billed. |
| invalid_request | error.type · 400 | Malformed request value (e.g. a non-UUID where a UUID is expected). |
| payload_too_large | error.type · 413 | Request body exceeds the limit. |
| limiter_unavailable | error.type · 503 | Transient limiter failure. Retry after a moment. |
Errors during streaming#
A stream: true request that fails mid-stream still returns HTTP 200, so the failure arrives as the final SSE event. The partial object holds the response received so far, and only that part is billed. With no partial output, data: [DONE] follows immediately.
data: {"error": {"code": "upstream_error", "message": "모델 제공자 연결이 중단되었습니다. 받은 부분까지만 과금됩니다.", "partial": {...}}}
data: [DONE]Retry guidance#
Retry 429 and 502 (retryable=true) with exponential backoff. The text path already retries providers twice and falls back, so we recommend a client retry interval of at least 1 second. 4xx and retryable=false 502 keep failing until you fix the request.
retryable=true, immediately retrying the identical request may yield the same result; check your parameters first.Common errors and fixes#
| Error | Fix |
|---|---|
400 — unsupported model ID (typo such as gpt-5.5-mini) | The message includes similar-model suggestions. Check the full list via GET /v1/models. |
| 400/422 — media parameter rejected (duration, resolution, reference count, etc.) | Check the per-model constraint tables in the media docs (Images, Video). The error message names the rejected parameter and its allowed values. |
| 402 — insufficient_credits | Top up in Dashboard → Billing and retry. For budget/quota codes, adjust the limit in key settings instead. |
| 429 — rate limit | Retry with exponential backoff after the Retry-After header value. |
| 502 — upstream_request_rejected | Inspect the upstream summary, check your request parameters, and switch models if the result repeats. |