Reference
Errors
Error shapes, status codes and retry rules for the inference API.
Updated 2026-10-25 · MainAI Compute docs
Two envelopesLink to this section
/v1 errors use the OpenAI shape. /api errors use the portal shape. Both carry an x-request-id response header.
/v1 error
json
{
"error": {
"message": "Model 'mainai-cyber' is not enabled for this key.",
"type": "permission_error",
"param": null,
"code": "model_not_allowed"
}
}/api error
json
{
"error": {
"code": "forbidden",
"message": "You need the billing role.",
"requestId": "req_01j9x7…"
}
}| Status | Meaning | Retry? |
|---|---|---|
| 400 | Malformed JSON, missing field or invalid value. | Fix the request. |
| 401 | Missing, malformed, revoked or expired key. | Fix credentials. |
| 402 | Wallet or key budget exhausted. | Top up or raise the limit. |
| 403 | Model not allowed, identity/plan gate, organization suspended or CSRF failure. | Depends; see code. |
| 404 | Unknown model or /v1 path. | Fix the model or path. |
| 413 | Body over 8 MiB. | Shrink or split. |
| 429 | Rate or concurrency limit. | Yes, with backoff. |
| 500 | Unexpected portal error. | Yes, bounded. |
| 502/503/504 | Engine or routing unavailable. | Yes, with backoff. |
400
- Meaning
- Malformed JSON, missing field or invalid value.
- Retry?
- Fix the request.
401
- Meaning
- Missing, malformed, revoked or expired key.
- Retry?
- Fix credentials.
402
- Meaning
- Wallet or key budget exhausted.
- Retry?
- Top up or raise the limit.
403
- Meaning
- Model not allowed, identity/plan gate, organization suspended or CSRF failure.
- Retry?
- Depends; see code.
404
- Meaning
- Unknown model or
/v1path. - Retry?
- Fix the model or path.
413
- Meaning
- Body over 8 MiB.
- Retry?
- Shrink or split.
429
- Meaning
- Rate or concurrency limit.
- Retry?
- Yes, with backoff.
500
- Meaning
- Unexpected portal error.
- Retry?
- Yes, bounded.
502/503/504
- Meaning
- Engine or routing unavailable.
- Retry?
- Yes, with backoff.
Stable MainAI codesLink to this section
code | Status | Cause |
|---|---|---|
invalid_api_key | 401 | Header missing, wrong scheme, wrong prefix, revoked or expired key. |
insufficient_credit | 402 | Wallet balance or hard key budget exhausted. |
org_suspended | 403 | Organization suspended by an operator. |
model_not_allowed | 403 | Model outside the key allow-list. |
kyc_required | 403 | Identity is not approved yet. |
contract_required | 403 | KYC is approved but no Enterprise contract is recorded. |
verification_required | 403 | Compatibility code for an offer whose access tier rejects the caller. |
model_not_found | 404 | Model ID is not in the public or private catalog. |
invalid_json | 400 | Body is not parseable JSON. |
payload_too_large | 413 | Body exceeds 8 MiB. |
not_found | 404 | Path is not one of the proxied /v1 routes. |
upstream_unavailable | 503 | The inference route failed or refused the connection. |
invalid_api_key
- Status
- 401
- Cause
- Header missing, wrong scheme, wrong prefix, revoked or expired key.
insufficient_credit
- Status
- 402
- Cause
- Wallet balance or hard key budget exhausted.
org_suspended
- Status
- 403
- Cause
- Organization suspended by an operator.
model_not_allowed
- Status
- 403
- Cause
- Model outside the key allow-list.
kyc_required
- Status
- 403
- Cause
- Identity is not approved yet.
contract_required
- Status
- 403
- Cause
- KYC is approved but no Enterprise contract is recorded.
verification_required
- Status
- 403
- Cause
- Compatibility code for an offer whose access tier rejects the caller.
model_not_found
- Status
- 404
- Cause
- Model ID is not in the public or private catalog.
invalid_json
- Status
- 400
- Cause
- Body is not parseable JSON.
payload_too_large
- Status
- 413
- Cause
- Body exceeds 8 MiB.
not_found
- Status
- 404
- Cause
- Path is not one of the proxied
/v1routes.
upstream_unavailable
- Status
- 503
- Cause
- The inference route failed or refused the connection.
Passthrough codesLink to this section
Once a request reaches an engine, the upstream error is passed through with its status and OpenAI-style code. Common examples are context_length_exceeded, rate_limit_exceeded, invalid_request_error and server_error. Read error.message; expect provider wording.
RetriesLink to this section
- Retry only
429,500,502,503and504, and only when the request is safe to repeat. - Use exponential backoff with jitter, starting around 250 ms, capped at 30 s.
- Honor
Retry-Afterwhen present. - Cap attempts. Four is usually enough for interactive calls.
- For streams, retry only if no content chunk has been emitted; otherwise the user has already seen partial output.
Bounded retry
typescript
async function callOnce(): Promise<Response> {
return fetch(`https://cloud.mainai.dk/v1/chat/completions`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.MAINAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "mainai-coder",
messages: [{ role: "user", content: "Say ready" }],
max_tokens: 8,
}),
});
}
async function callWithRetry() {
for (let attempt = 0; attempt < 4; attempt++) {
const res = await callOnce();
if (![429, 500, 502, 503, 504].includes(res.status)) return res;
const retryAfter = Number(res.headers.get("retry-after"));
const wait = Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter * 1000
: 250 * 2 ** attempt + Math.random() * 200;
await new Promise((resolve) => setTimeout(resolve, wait));
}
return callOnce();
}Debugging checklistLink to this section
- 01Record status,
error.codeandx-request-id. - 02Confirm the key prefix and that the key is active.
- 03Call
GET /v1/modelswith the same key to see its allow-list. - 04Check the body size and JSON validity.
- 05Check the wallet and organization status in the portal.
- 06Escalate with the request ID if the failure repeats without a local cause.