Every error comes back as a problem detail with a status, a type, a readable detail, a request_id, and a retryability hint (Errors). Branch on status, type, and retryability, never on the wording of detail.
A handler
import httpx
def check(response: httpx.Response) -> dict:
if response.status_code < 400:
return response.json()
problem = response.json()
kind = problem.get("type", "").rsplit("/", 1)[-1]
hint = problem.get("retryability")
message = f"{problem.get('status')} {kind}: {problem.get('detail')} (request {problem.get('request_id')})"
if hint in {"upstream", "timeout"}:
raise TemporaryError(message) # retry with backoff
if hint == "quota":
raise QuotaError(message) # wait, top up, or raise a limit
if response.status_code == 422:
raise InvalidRequest(message, problem.get("errors", []))
raise PermanentError(message) # fix the request or the credentialWith the Python SDK, errors are exceptions: BadRequestError (400), AuthenticationError (401), PaymentRequiredError (402), ForbiddenError (403), NotFoundError (404), ConflictError (409), ValidationError (422), RateLimitError (429), and ServerError (5xx), all subclasses of CogniChemError with status, code (the type slug), detail, request_id, retryability, and errors.
Errors you'll meet
| You see | Likely cause | Fix |
|---|---|---|
400 missing-idempotency-key | An API-key write without Idempotency-Key | Send one (Idempotency) |
400 on submit with a wallet detail | Balance minus reservations can't cover the estimate | Add funds, or wait for running work to finish |
400 on submit, duplicate name | job_name already used | Choose a new name |
401 | Key missing, wrong, revoked, or expired | Check COGNICHEM_API_KEY, or create a new key |
403 insufficient-scope | A read key calling a write | Use a key with write |
403 spend-ceiling-exceeded | The key's monthly ceiling would be passed | Raise the ceiling or wait for next month |
403 with a monthly limit detail | Utility or inference allowance used up | Wait for next month or change plan |
409 | Same idempotency key with a different body, or a request still in progress | Use a new key for a new request; otherwise retry later |
413 | A file or request over the size limit | Upload large files as artifacts and pass references |
422 | The request or payload failed validation | Read errors; each tool's payload fields are on its tool page |
429 | Over the rate limit | Back off for a minute |
5xx | A temporary problem on our side | Retry with backoff; quote request_id if it persists |
Jobs that fail
A job that ends error isn't an HTTP error: its status message says what went wrong (often bad inputs the tool couldn't process). Check the payload against the tool's fields, fix it, and submit again under a new job_name. Failed jobs are charged only for the time they ran (Jobs).