# How failures reach the client

Every failure reaches the client in one envelope: `code`, `message`,
`request_id`, and the failure's client fields under `details`
([Failures](failures.md)). The status comes from the failure's kind, and
the `x-request-id` header repeats the id.

Bad input lists each problem without its value. It names the field by its
path in the `Input`, with list positions as numbers, the names the OpenAPI
schema uses:

```json
{
  "error": {
    "code": "invalid_input",
    "message": "Request input is invalid.",
    "request_id": "req_0b1e…",
    "details": {
      "problems": [
        { "field": "lines.2.quantity", "rule": "max" },
        { "field": "colour", "rule": "unknown_field" }
      ]
    }
  }
}
```

The runtime answers these itself, before or around what the route runs:

| Condition | Status and code |
|---|---|
| The body is not JSON, isn't sent as JSON (`Content-Type`), or the query string cannot be read | 400 `invalid_request` |
| A header's value isn't printable ASCII | 400 `invalid_request` |
| A field is missing, unknown or breaks a rule (body, query, header or placeholder), or the route reads no query string or body and one came | 422 `invalid_input` |
| No route matches | 404 `route_not_found` |
| The path exists but not for this method | 405 `method_not_allowed`, with `Allow` |
| The body is over the limit | 413 `request_too_large` |
| The deadline passed | 504 `deadline_exceeded` |
| An `Idempotency-Key` is missing where the API requires one, or isn't 1 to 255 printable ASCII characters | 400 `invalid_request` |
| An `Idempotency-Key` comes again with a different request | 422 `idempotency_key_reused` |
| An `Idempotency-Key` comes again while its first request is running | 409 `conflict` |

## When a repeat is the same request

A request with a key that came before is the same request when its route
(its method and declared path) and what it says, decoded, are the same.
What it says is each placeholder and the input: from the query string, the
headers it reads and the body. The runtime writes these back through the
route's own codecs as the contract's JSON, and keeps a digest of that.

That JSON writes every secret value (`Password`, `BearerToken` and the
rest, see [Standard library](standard-library.md)) as its kind alone. So
the digest the runtime keeps says nothing of a secret, whether it came in
the body or the path, and whoever can read the key table can't test
guessed passwords against it. A repeat that differs only in a secret, or
only in its JSON's spacing or member order, is the same request, answered
as the first was.

A response that starts a session or carries a credential is never kept.

The digest isn't keyed (no secret is mixed into the hash), since a key
would need a secret every deployment holds, and Jadpo has none
([Configuration and time](configuration-and-time.md)). So a value that
isn't secret, sent and neither stored nor answered, can still be confirmed
by guessing, by whoever reads the table. That person reads the kept
responses and the application's own tables as well.

## When the host has no clock

A deployable, a program `jadpo build` makes from the app such as a native
server ([Declared deployables](declared-deployables.md)),
answers under its host's clock. When the clock can't be read (the system
clock is before 1970), every request, health checks included, is 500
`internal_fault` and nothing runs. Its fault, for the host's log, says the
host has no clock. `jadpo dev` doesn't start (`cli.dev_start_failed`, "The
system clock can't be read").

## Wording for people

The envelope's `message` stays fixed English. Text for people in client
apps comes from the app's language files
([Failures for client apps](failures-for-client-apps.md)).
Every failure a route can produce has wording in every supported locale,
keyed by its code, with its client fields as placeholders. Client fields
that are dates or numbers stay values on the wire, for the client to
format in its own language.
