The envelope
code— the stable identifier, one of 26. The only field to branch on.category— one of eight. It selects the SDK exception class.problem,cause,fix— what went wrong, why, and what to do. Written for a human.docUrl— the documentation anchor for this code.retryable—truefor four codes. See Retrying.requestId—req_plus 16 hex characters, logged server-side under the same id.
code. Never branch on message text — problem, cause and fix are written for
a human and the server rewrites them per instance, so a malformed CSV names the offending row
and column in its cause.
Not every error is an envelope
400 like that one — or {"error":"invalid_request"}. No code, no category, no
requestId.
So “every error carries a request id” is false. Typed envelopes carry one; malformed input
does not, and there is nothing to quote to support.
- The SDK cannot type these. With no
codeto resolve, it falls back to the baseHollerithErrorcarrying the textAn unexpected error occurred.Anexcept ValidationErrorblock will not catch it. - Catch
HollerithErrorat the outer edge of any integration, not only the subclasses you expect. - A
400with no code means the body shape was wrong, most often a misspelled field. A typo in the inlinePOST /v1/predictionsbody falls through to the context-backed parser and returnsinvalid_requestwithout saying which shape failed.
Categories and SDK exceptions
Thecategory field, not the code, selects the exception class. Eight categories, eight
classes, all subclassing HollerithError.
code, problem, cause, fix, doc_url, retryable and
request_id. Printing one renders all of them on separate lines.
The catalogue
TheWhat to do column is the fix string the API returns for that code.
auth — 401, AuthenticationError
permission — 403, PermissionDeniedError
quota — 429, QuotaExceededError
validation — 413 and 422, ValidationError
fitted_context_incompatible is a validation error, not a not_found one, despite its name
sitting beside three context codes below. It raises ValidationError.
not_found — 404, NotFoundError
Reading
/v1/predictions/{id}/result before the job succeeds returns job_not_found, not a
409 or a 425. Poll the job until its status is succeeded, then read the result.
conflict — 409, ConflictError
unavailable — 503, ServiceUnavailableError
worker_warming_up is defined in the contract but nothing in the control plane emits it. A
cold or unreachable worker reaches you as worker_unavailable instead, which the SDK also
raises itself when it cannot reach the API or object storage.
server — 500, ServerError
Retrying
Exactly four codes are retryable:rate_limited, worker_warming_up, worker_unavailable
and cache_unavailable. Retry those, unchanged. Every other code will return the same answer
however many times you send it.
quota_exceeded is a 429 that is not retryable. A client that retries on status code rather
than on the retryable field will loop against it until the daily reset.
The SDK has no backoff-retry layer. What it does is narrower than that, in three ways:
- Only
ServiceUnavailableErrorwithretryable=trueis absorbed. AQuotaExceededErroris raised to you on the first occurrence. - Only inside a poll loop. A 503 on the call that submits the job reaches you; a 503 while polling an already-submitted job is swallowed.
- At a fixed
poll_interval, default1.0second, with no exponential growth, untiltimeout(default900.0seconds) lapses and aTimeoutErroris raised.
predict() and evaluate() always absorb a retryable 503 during
polling, fit() only does so when you pass on_warming=. Without that hook, a transient
warm-up during a fit wait propagates.
Two codes that carry more than one meaning
training_ref_expired
One code, two situations: the staged input for a job expired before the worker read it, and a
succeeded job whose result aged out. The first needs a re-run, the second means you read the
result too late.
Scored predictions and evaluation metrics live for 1 hour. Fetch them inside that window or
re-run the job.
rate_limited
This code is currently never emitted. No rate limiter is wired for the public API, so it is
defined and typed but unreachable today.
Handle it anyway. It costs one branch, and its retryable flag is true when it does arrive.
When you contact support
Send therequestId. The API logs the same id server-side against the failing request, so it
is the fastest way to the exact log line.
invalid_json or invalid_request response carries none,
and errors the SDK raises before any request — missing_api_key is the common one — have
request_id set to None. Quote the code and the call you made instead.
Related
- Limits — the ceilings behind
dataset_too_largeandpayload_too_large - The fitted context — expiry, and the three context codes
- Usage and billing — what
quota_exceededis counting - Troubleshooting — symptoms rather than codes