# Kinds

Every failure has a kind, which decides the HTTP status a client gets and
what the failure means. A kind is a `FailureKind` value, written
`FailureKind.not_found` ([Enums](enums.md)).

You can declare seven of the kinds. The runtime raises the other eight
itself, and declaring one of those is the error
`failure.kind_runtime_only`.

| Kind | HTTP | Meaning | Raised by |
|---|---:|---|---|
| `invalid_request` | 400 | The request could not be read (bad JSON, wrong shape) | runtime |
| `invalid_value` | 422 | A well-formed value breaks a rule | you; the runtime for request input |
| `unauthenticated` | 401 | No valid identity | runtime ([Identity](identity.md)) |
| `not_permitted` | 403 | The caller is known but may not do this | you; [policy](policy.md) |
| `not_visible` | 404 | Hidden from this caller | policy only ([Concealing records](concealing-records.md)) |
| `not_found` | 404 | The record does not exist | you |
| `conflict` | 409 | The current state conflicts with the request | you |
| `precondition_failed` | 412 | An HTTP precondition such as `If-Match` failed | runtime ([HTTP](http.md)) |
| `rejected` | 422 | A business rule refused the operation | you |
| `rate_limited` | 429 | Wait before retrying | you; runtime |
| `unavailable` | 503 | A dependency is temporarily unavailable | you; runtime |
| `timed_out` | 504 | A dependency or deadline ran out of time | runtime |
| `outcome_unknown` | 500 | The change may or may not have happened, so retrying blindly is unsafe | runtime |
| `misconfigured` | 500 | Deployment configuration is wrong | runtime |
| `internal_fault` | 500 | A defect | runtime |

Here `CheckoutUnavailable` is fine, because a checkout that is briefly
unavailable is a business outcome callers handle. `Broken` is refused,
because only the runtime raises `internal_fault`:

```jadpo
type CheckoutUnavailable = Failure {
    kind: FailureKind.unavailable  // fine: a business outcome callers handle
    message: "Checkout is temporarily unavailable."
}

type Broken = Failure {
    kind: FailureKind.internal_fault  // error: only the runtime raises internal_fault (question 3)
}
```

```text
error: `Broken` can't have the kind `internal_fault`
 --> main.jadpo:7:11
  |
7 |     kind: FailureKind.internal_fault  // error: only the runtime raises internal_fault (question 3)
  |           ^^^^^^^^^^^^^^^^^^^^^^^^^^ raised only by the runtime
  reason: The runtime raises `internal_fault` itself; an application declares `invalid_value`, `not_found`, `not_permitted`, `conflict`, `rejected`, `rate_limited` and `unavailable`, so a client's status always means the same thing.
  next: Use the kind an application declares that fits this outcome (agent)
  rule: failure.kind_runtime_only
  help: https://jadpo.dev/docs/errors/failure.kind_runtime_only

check failed: 1 error, 1 file
```

`invalid_value` and `rejected` both use 422, and 400 is kept for requests
that can't be read at all. An application can't add a kind or choose a
status. If no kind fits, that is a change to the language, not to your
app.
