Errors
Error model, status codes, and recommended handling.
CanalAPI returns errors in the same shape as OpenAI: a non-2xx HTTP status code plus a JSON body containing an error object.
{
"error": {
"message": "Invalid API key.",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}HTTP status codes
| Status | Meaning | Typical cause |
|---|---|---|
| 400 | Bad Request | Malformed JSON, missing required fields, invalid parameters. |
| 401 | Unauthorized | Missing, malformed, or revoked API key. |
| 403 | Forbidden | Valid key but model not allowed / IP not in allowlist / key disabled. |
| 404 | Not Found | Unknown endpoint or model. |
| 408 | Request Timeout | Client took too long to send the request. |
| 409 | Conflict | Idempotency or state conflict. |
| 413 | Payload Too Large | Request body or context exceeds limits. |
| 422 | Unprocessable Entity | Validation error on a structurally valid request. |
| 429 | Too Many Requests | Rate limit or quota exceeded. |
| 500 | Internal Server Error | Unexpected server-side error — safe to retry with backoff. |
| 502 / 503 / 504 | Bad Gateway / Service Unavailable / Gateway Timeout | Upstream provider problem — retry with backoff. |
Error fields
error.message— human-readable description (do not string-match on it in production code).error.type— broad category, e.g.invalid_request_error,rate_limit_error,api_error.error.code— machine-readable code; branch on this.
code can be either an OpenAI-style string (invalid_api_key, rate_limit_exceeded, …) or a numeric platform error code.
Platform error-code ranges
When the error originates from the CanalAPI platform (rather than the upstream model), code is numbered into ranges so you can quickly identify the failing domain:
| Range | Numbers | Domain |
|---|---|---|
| 1xxx | 1000 – 1099 | Auth / account / API key / quota (e.g. key revoked, account disabled, quota exhausted) |
| 2xxx | 2100 – 2199 | Gateway / channel / adapter (e.g. model not found, upstream unreachable) |
| 3xxx | 3000 – 3099 | Billing / balance / pricing (e.g. insufficient balance, missing price) |
| 4xxx | 4000 – 4099 | Top-up / payment orders (e.g. order timeout, webhook validation failed) |
| 5xxx | 5000 – 5099 | Log query / export |
| 6xxx | 6000 – 6099 | Invoice generation / billing profile |
| 7xxx | 7000 – 7099 | Referrals / withdrawals |
| 8xxx | 8000 – 8099 | Notifications / announcements |
| 11xxx | 11500 – 11599 | Subscription plans / orders |
On the API call path you'll typically see 1xxx, 2xxx, and 3xxx; 4xxx and above mostly appear during console flows (top-up, invoice, withdrawal, subscription).
Retryable vs non-retryable
- Retryable:
408,429,500,502,503,504.409depends on the situation. - Do not retry without changing the request:
400,401,403,404,413,422.
Use exponential backoff with jitter (for example: 1s, 2s, 4s, 8s, max ~30s, plus ±25% jitter). Cap the number of retries (typically 5). When 429 returns a Retry-After header, respect it — see Rate Limits.
Reporting
When opening a support ticket, include:
- The request ID (from response headers and visible in Usage Logs)
- A rough timestamp (UTC or local timezone)
- The model and endpoint used
This drastically shortens diagnosis. CanalAPI never includes your prompt / completion content in tickets.