Errors and retries
{ "error": { "type": "conflict", "message": "v7 is live, not v6. Check what changed, then send `expect: \"v7\"`.", "param": "expect", "request_id": "req_8a4f2fedbd4af9da" }}Every error has this body. message is written for a person and says what to do. param names the request field the error is about, such as cases[0].input, or is null. request_id is also in the Pensieve-Request-Id header of every response; quote it when you ask for help.
| Status | type |
Means | Retry? |
|---|---|---|---|
| 400 | invalid_request |
A field is missing, unknown or wrong; param names it |
No: fix the request |
| 401 | authentication |
No key, an unknown key, or a revoked one | No |
| 403 | permission |
The key can’t do this | No |
| 404 | not_found |
No such model, call, release or route | No |
| 409 | conflict |
The object’s state refuses it, such as promoting before sign-off, or a stale expect |
No: read the state, then decide |
| 429 | rate_limit |
Too many requests; Retry-After says when to try again |
Yes |
| 501 | not_available |
Part of the API, but not served here yet | No |
| 500 | server |
Something failed on Pensieve’s side | Yes |
Retries
Section titled “Retries”The SDKs try again on their own, twice by default: after a dropped connection, a timeout, or a 408, 429, 500, 502, 503 or 504. They wait half a second, then a second, doubling up to 8 seconds, with some jitter. A Retry-After header wins, up to a minute. They never retry a 501 or any other 4xx.
Idempotency
Section titled “Idempotency”Every POST takes an Idempotency-Key header. Send the same key again within 24 hours and you get the first answer back, with Idempotent-Replayed: true, instead of the work being done twice. Send the same key with a different request and you get a conflict.
The SDKs make up a key for every POST and keep it across their retries. Pass your own to make a retry safe across processes, such as a job that may run twice:
await pensieve.models.decide("grade-audit", { input }, { idempotencyKey: `grade-${trial.task}` });client.models.decide("grade-audit", input=case, idempotency_key=f"grade-{case['task']}")Rate limits
Section titled “Rate limits”The sandbox allows 300 requests per key every 10 seconds. Live limits are set per workspace.