Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 30 Oct 2026. What's built · Install 0.1.0
jadpo
Reference: Failures

Failures

Declaring a failure

On this page

A failure is a way an action or query can fail that its caller is expected to handle, such as a todo that doesn't exist. You declare it as a type whose base is Failure, written type TodoNotFound = Failure { ... }. The base gives the type its settings: kind, message, client, internal and, rarely, code. Here are two failures, with the Todo and Product entities (stored records) and the Quantity type they refer to declared elsewhere:

example
/// The todo does not exist.type TodoNotFound = Failure {    kind: FailureKind.not_found    message: "Todo not found."    internal: {        todo_id: Todo.id    }}type QuantityUnavailable = Failure {    kind: FailureKind.conflict    message: "The requested quantity is unavailable."    client: {        available: Quantity    }    internal: {        product_id: Product.id        requested: Quantity    }}

The /// line above TodoNotFound is a documentation comment. It is optional, and becomes the failure's description in generated documentation and OpenAPI. A field typed Todo.id has the type of Todo's id field.

kind:, which sets the status

kind: names one of the kinds, the categories a failure falls into, such as not_found or conflict. The kind sets the HTTP status a client gets, and how background work, such as a job or an event subscriber, treats the failure. A failure never states a status itself.

The code clients see

A failure's public code is its name in lower_snake_case: TodoNotFound is todo_not_found. Clients rely on it, so it stays the same. Write code: "todo_missing" only to keep a code clients already use after you rename the failure.

message:

message: is fixed text that is safe to show anyone. It can't include values. Leave it out and the client gets the generic message Jadpo keeps for the failure's kind.

client: and internal: fields

A failure can carry values that say what went wrong, such as which product ran out. These are its fields, and they go in one of two blocks:

  • client: { } holds the fields sent to the caller.
  • internal: { } holds the fields that never leave the server. They are for your logs and traces.

A field name can't be in both. A failure has no fields outside these two blocks. The caller gets a JSON body with code, message, request_id and, when the failure has client fields, those fields under details (How failures reach the client). Leave client out and the body has only the code, the message and the request id.

These fields are ordinary fields. A T? type, which may hold no value, is allowed. optional and default are not, because only Input types may have them (One form for every type).

Where the file goes

An app's code is split into domains: one business area each, such as planning, in a folder under domains/ (Project layout). Each failure has its own file in its domain's failures/ folder, such as domains/planning/failures/todo_not_found.jadpo.

When the actions of two or more domains can fail with the same failure, you put its file in shared/failures/ instead. When code in another domain handles the failure, for example in a match, write public before the declaration: public type QuantityUnavailable = Failure { ... }.

Markdown for agents: /docs/declaring-a-failure.md.Built from Jadpo main at a707efb.