Errors
Handle request failures using HTTP status codes and typed error tags.
A failure is always a JSON body with a _tag and the error's own fields, exactly the
shape the contract declares.
{
"_tag": "Forbidden",
"required": "control",
"held": ["read", "data"]
}Branch on the tag. The status is a summary of something you can already read, and it is a pure function of the tag, so the same error means the same status wherever it is raised.
| Status | Meaning | Examples |
|---|---|---|
| 400 | The request is not acceptable input. A body that does not decode lands here as BadRequest. | MetadataFilterInvalid, MetadataTooLarge, BlobTooLarge, TtlTooLong, RetentionInvalid |
| 401 | No usable credential. | Unauthorized, carrying a reason: unknown, revoked, expired |
| 402 | The spend cap. | SpendLimitExceeded |
| 403 | A credential that is fine, refused for what it asked. | Forbidden, carrying required and held for a scope, or a reason of not_a_member, environment_mismatch (an environment header conflicts with the API key) |
| 404 | It is not here. | every *NotFound |
| 409 | It is already there, or it moved. | ProviderSlugTaken, ConnectionSlugTaken, EnvironmentSlugTaken, VersionConflict |
| 422 | Understood, and refused on its merits. | OverrideNotAllowed, ProviderInUse, SecretInUse, AgentNotReady, AgentCopyUnresolved, DecisionExpired, ConnectionError, ProviderError |
Decide whether to retry
For SpendLimitExceeded (402), raise the cap or wait until resetsAt.
For VersionConflict (409), read the current version before submitting another update.
For OverrideNotAllowed (422), change the requested override or the agent's policy.
A not-found response can mean the resource does not exist in the selected environment.
Use a stable idempotency key when retrying a session creation or message send after a transport failure. See delivery modes.
In the SDK
Typed failures arrive as CerealError subclasses, each carrying the wire tag and
the server's fields under data.
import { SpendLimitExceededError, DecisionExpiredError } from "@trycereal/sdk"
try {
await client.sessions.send({ id, message })
} catch (error) {
if (error instanceof SpendLimitExceededError) {
console.warn(`${error.scope} out of ${error.metric} until ${error.resetsAt}`)
return
}
throw error
}A tag this version of the SDK does not know still arrives as a CerealError with that
tag, so a contract addition never breaks an older client. Transport and validation
problems are CerealTransportError and CerealValidationError.