Chersus

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.