Documentation
Errors
Every error Chersus returns: 401, 429, validation rejections, and 5xx. What each means, and what to do about it. One envelope, one code to branch on.
Last updated
Chersus fails in two places, and the difference decides how you handle it. We reject a
malformed request before anything runs, or a run fails part-way through a pipeline that
had already started. The first is an HTTP error. The second is a 200 whose body says
status: "failed".
Branch on error.code, never on the message. Messages are for humans; we can reword
them at any time. Codes are part of the contract.
The envelope
Every error, at either level, carries the same shape. details appears when there is
something specific to point at, such as the step that could not run:
{
"error": {
"code": "invalid_pipeline",
"message": "Step 'tox' cannot accept the payload that step 'extract' emits.",
"details": { "step": "tox" }
}
}
Rejected before anything runs
The request never reaches a model, so Chersus bills nothing.
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
invalid_api_key |
401 |
The key is missing, malformed, or revoked | Check the Authorization header. A key that worked yesterday and fails today usually means someone rotated it: authentication |
insufficient_quota |
402 |
The month’s character allowance is exhausted | Raise the tier or wait for the reset. Do not retry: the answer will not change until one of those happens. The pricing page has the rate table |
rate_limited |
429 |
Too many requests in the current window | Back off until Retry-After seconds have passed, then retry. See rate limits |
invalid_request |
400 |
The body is malformed, or a required field is missing | Fix the request. details.field names the field when one is at fault |
unknown_service |
400 |
A step names a service that does not exist | Check the spelling against the services directory. details.step names the step |
invalid_pipeline |
400 |
The pipeline cannot succeed as written: one step emits a type the next does not accept, or a stop_if sits on a step that returns no verdict |
Reorder or drop the offending step. details.step names it |
input_too_large |
413 |
The input is over 100,000 characters | Split the input and run it in parts |
The four 400 and 413 codes all come from the validator, which runs before any
compute. A rejected request costs nothing, so validating client-side is an
optimization for latency, never for cost.
Retry only 429 and 5xx. The rest describe something about the request that a retry
will not change.
A server-side failure returns 5xx with the same envelope. Chersus bills nothing, and
a retry is safe: use an Idempotency-Key so a retry racing the original does not run
twice.
Exhausted quota returns 402, not the more common 429, and the choice is deliberate.
The two need different handling. A 429 means come back shortly and the same request
will work; a 402 means nothing will change until the tier does. Folding them together
leaves a client retrying a request that cannot succeed until somebody upgrades an
account.
Failed part-way through a run
The request was valid and the run started, so the response is a 200 carrying
status: "failed". An error object replaces output, and results still holds
every step that completed before the failure.
{
"trace_id": "chs_01J9X4Q7K2M8N3P5R6T7V8W9X0",
"status": "failed",
"error": {
"code": "step_failed",
"message": "The step could not process the payload it was given.",
"details": { "step": "tox" }
},
"results": {
"redact": { "kind": "transform", "status": "ok", "changes": 2 }
},
"usage": {
"billed": [
{ "unit": "characters", "quantity": 61, "steps": ["redact"] }
]
}
}
Only the completed steps bill. Read results to see how far the run got, and
usage.billed to see what it cost. Same fact, one in steps and one in money.
A run-level failure is always step_failed, and details.step names the step that
could not finish. We ship exactly one code here on purpose. Whichever service failed,
your action is the same: read results for what completed and decide whether to rerun
the rest.
A failure on Chersus’s own side never arrives this way. Those return 5xx, bill
nothing, and are safe to retry.
Not errors, though they look like it
Two outcomes return 200 and are easy to mistake for failures.
A halted run is a success. A stop_if fired, or a workflow hit a cap, so the run
stopped early on purpose. status is "halted", halted_at names the last step that
ran, and output is the payload as it stood. Steps that never ran never bill. This is
the jailbreak shield doing its job, not an error.
A duplicate is also a success. A repeat with an Idempotency-Key from the last 24
hours returns status: "duplicate" with the original trace_id. Nothing ran and
Chersus billed nothing, so keep the first response you got.
What never bills
Worth stating in one place, because it spans all of the above.
- A rejected request, at any of the codes in the first table.
- A failure on Chersus’s side.
- Any step that did not run, whether the run halted or failed before reaching it.
A run that fails part-way bills the steps that completed, and nothing else. The chaining guide covers how that interacts with a pipeline.