code to learn what happened, and its retryable to learn whether to send the call again.
The error body
Errors use one envelope:{
"error": {
"code": "invalid_request",
"message": "The request does not match the schema.",
"retryable": false,
"request_id": "req_example",
"details": [{"path": ["body", "decisions"], "message": "Check this field.", "type": "example"}]
}
}
error.code and error.retryable, not on the status alone. One status can have several causes. A 503 can be a brief failure on our side, or a model this server will not serve.
retryable: truemeans a later attempt may succeed. It does not mean an earlier attempt did no work.detailsnames the rejected fields of a422.- When the response has a
Retry-Afterheader, wait that long before you try again.
retryable is true, and send the same body with the same Idempotency-Key. Retries and idempotency explains when to retry, and how a key stops a call from running twice.
Quote the X-Request-ID response header when you write to help@sqwish.ai about a call.
What does this status code mean?
| Status | Meaning | Retry? | Billed? |
|---|---|---|---|
200 | Answered. The response includes the decision and its charge | Only if the reply was lost: same key and body | Once. A replay isn’t charged again, except under zero data retention |
400 | The body isn’t valid JSON, repeats a key, holds NaN or Infinity, or isn’t valid data for its Content-Encoding | No. Fix the body | No |
401 | The key is missing, malformed or revoked. A browser session is not an API key | No. Send a valid key | No |
402 | Available credit is below this operation’s maximum cost. Or the project reached a spending limit that stops its new work. Credit held for work in progress can’t be spent until that work ends | No. Add credit, wait until a hold ends, or raise the limit | No. Nothing was reserved |
403 | The account is waitlisted or blocked, or the caller can’t use this route | No | No |
409 | An idempotency conflict, a call still running or with an unknown outcome, an expired saved answer, or a family or model that doesn’t answer calls | Only request_in_progress, after Retry-After | No new charge. The original call settles on its own |
413 | The body is over the limit: 1 MiB for JSON, 48 MiB for a decision with images, your tier’s upload_bytes for a dataset form, or 16 MiB for a labelling source | No. Send less, or buy credit to reach a tier that takes a larger dataset form | No |
415 | The content type isn’t accepted. Or the body’s content encoding is neither gzip nor zstd, or it has more than one. Or a compressed body came in an unauthenticated request, or to a route that needs no authentication | No | No |
422 | A decision, field or value was rejected. error.details names the fields | No. Change the body, and send it with a new Idempotency-Key | No |
429 | Over the account’s request, token or concurrency budget, or no room to start a job | Yes, after Retry-After | No |
503 | Unavailable. The service failed for a moment, the rate limiter’s store is down, or the model isn’t served here | Only when retryable is true, after Retry-After, with the same key and body | No. A failure after admission releases the reservation |
504 | The model didn’t answer in time. The call may already have been accepted | Yes, with the same Idempotency-Key and body | A success is billed once. A replay isn’t a second charge, except under zero data retention |
422 also covers text that holds a NUL character or half of a UTF-16 surrogate pair. A string cut in the middle of an emoji can leave half a pair.
A 504 doesn’t prove that the server stopped, or that nothing was charged. So save the Idempotency-Key before the first attempt, and resend the call with it (After a lost response).
Error codes
These codes each need handling of their own. Each row says what happened and what to do.| Code | Status | What happened, and what to do |
|---|---|---|
insufficient_credit | 402 | The credit detail says what the operation needed and how much is available. Add credit, or wait until the hold ends. Then send the request again. With a new Idempotency-Key, it is a new operation |
project_spend_limit | 402 | The project’s spending limit stopped its new work this month. Work already running carries on. Ask an owner or billing member to raise the limit, or wait for the next month in UTC (Stop new work at the limit) |
key_spend_limit | 402 | The decision would take the key past its monthly cap (Monthly caps) |
role_forbidden | 403 | In a shared organisation, the caller’s role doesn’t allow this route. For example, a project key can’t reach the wallet. A project key works as a developer, unless it is an older key whose role is billing (Keys in an organisation) |
key_project | 403 | The OneHot-Project header names a project the key doesn’t work in. Leave the header out. A project key works in its own project, and an admin key in none (Keys and projects) |
key_scope | 403 | The key’s access doesn’t allow this route. Use a key whose access does (Access levels) |
console_only | 403 | Only the console can do this. Account changes and the download of the whole account are two examples (What a key can’t do) |
idempotency_conflict | 409 | The body changed under the same key. Keep the original body, or start a new operation with a new key |
request_in_progress | 409 | The original call is still running. A first call can get it too, when the server couldn’t confirm that its answer was stored. Retry with the same key after Retry-After |
outcome_unknown | 409 | The same case, for a call sent without an Idempotency-Key. It may have been answered and charged, so don’t send it again until you have looked it up. Find the response’s X-Operation-ID in your request history. succeeded with a cost means it was answered and charged. failed means you can send it again. accepted means it hasn’t ended yet |
idempotency_response_expired | 409 | The saved answer is gone, and that key won’t run the decision again. Use a new key if you still want an answer |
family_off, model_off | 409 | What you called is switched off. Switch it on, then call again (Switch a family or a model on) |
family_empty | 409 | The family serves no model yet. It moves to its first model once one joins. Meanwhile, a model whose switch is on answers calls to its number. The message says what to do. While the fine-tune is queued or training, wait. If the fine-tune failed its gate, failed or was cancelled, continue the family or start a new one |
route_gone | 410 | The route moved or is gone. The message names the call to make instead, such as GET /v1/organization/usage (Moved routes). Retrying doesn’t help |
context_too_long | 422 | The request is over the input limit of 65,536 tokens. Rarely, a shorter request gets the same error too. Tokens are counted as they are billed, and the limit is the same for every model. Nothing was charged, and the same body is refused again. Send a shorter context, fewer decisions, or fewer or smaller images (Input tokens) |
too_many_tokens | 422 | The request has more input tokens than your account allows in a minute. For a decision, that is your tier’s tpm. For an estimate, it is estimate_tokens_per_minute. Both are in effective_limits on GET /v1/account. No wait admits it, so retryable is false. Split it into smaller requests, each a new operation. For a decision, you can also buy credit to reach a higher tier. Nothing was charged, and the request was never accepted. So the same Idempotency-Key with the same body can still run it |
rate_limited | 429 | The message names the limit you are over and when it resets. Wait for Retry-After. Your account’s limits are on GET /v1/account (Rate limits) |
tenant_queue_full, queue_full | 429 | There was no room to start a job. With tenant_queue_full, your account already has as many jobs of that kind queued, running or cancelling as it can: fine-tunes or labelling jobs. For fine-tunes that is effective_limits.fine_tunes on GET /v1/account (account limits). With queue_full, the queue that every account shares is full, for fine-tunes, labelling or evaluations. An evaluation also gets it when your account has too many evaluations running. Wait for a job to finish, then start yours again |
tokens_counting | 503 | A fine-tune’s estimate or start came while a large dataset version’s training tokens are first counted. Nothing was started or held. Wait for Retry-After, then send the same body with the same Idempotency-Key. The count goes on meanwhile, and the start is made once |
long_context_unavailable | 503 | Requests as long as yours can’t be served at the moment. They can be again when the service is whole. Nothing was charged, and the Idempotency-Key isn’t used up. Wait for Retry-After, then send the same body with the same key. Or send a shorter context |
model_warming | 503 | A fine-tuned model that wasn’t loaded is still loading after 10 seconds, and the request said "fallback": "none". Nothing was charged. The Idempotency-Key keeps this answer, so retryable is false. Retry-After is 5 seconds. Wait that long, then send the same body with a new Idempotency-Key (Readiness and cold starts) |
stream_not_supported | 422 | /v1/systemone was sent stream: true. It answers in one JSON body. Leave out stream, or send it as false |
n_not_supported | 422 | /v1/systemone was sent n other than 1. It gives one answer to each question |
unknown_field | 422 | /v1/systemone was sent a likely misspelling of a field, without the field itself. The fields are state, assets, and a question’s type, instructions and criteria. A likely misspelling is a name at most two letters away, ignoring case, such as State or criterias. The message names the field it likely means. Rename yours, or send the real field beside it |
