# `ProblemDetails`

An HTTP API's error body, as other APIs often send one.

Use it to read errors from an outside service. In your own routes, declare failures instead.

## Shape

| Field | Type | Required? |
|---|---|---|
| `type` | [`Url`](/docs/types/type-url.md) | No |
| `status` | [`HttpStatus`](/docs/types/type-http-status.md) | No |
| `title` | `Text` | No |
| `detail` | `Text` | No |
| `instance` | [`Url`](/docs/types/type-url.md) | No |

Write every field when you build one, and give a field that isn't required as `none`.

## Example

```jadpo
ProblemDetails {
    type: none
    status: 404
    title: "Not Found"
    detail: none
    instance: none
}
```

## Good to know

- `==` tells you whether two are the same. `<` and `list.sort_by` don't take it, so sort by one of its parts instead.

## In JSON

An object of its parts, in a request, a response or `json.write`:

```json
{"type":"https://example.com/probs/out-of-credit","status":403,"title":"You do not have enough credit.","detail":"You have 30 credits, but that costs 50.","instance":"https://example.net/account/12345/msgs/abc"}
{"status":404,"title":"Not Found"}
{"status":503}
```

When you read one, a field that isn't required may be left out, or sent as `null`.

## Details

<details>
<summary>Text form, storage, how it is shown, and operations</summary>

`ProblemDetails` is in the group Web and network, and has been in Jadpo since 0.1.0.

### Text form

It has no single text form, so `text.from` and `text.parse` don't take it.

### How it is stored

It is stored in 5 columns, each named for the field, `_` and its suffix. This is `{"status":503}`:

| Column | Type | Holds |
|---|---|---|
| `<field>_type` | `text` | `NULL::text` |
| `<field>_status` | `smallint` | `503` |
| `<field>_title` | `text` | `NULL::text` |
| `<field>_detail` | `text` | `NULL::text` |
| `<field>_instance` | `text` | `NULL::text` |

### How it is shown to people

`text.format(value, locale:)` shows it to people in their locale.

With `locale: Locale.en_gb`:

| Value | Shown |
|---|---|
| `{"type":"https://example.com/probs/out-of-credit","status":403,"title":"You do not have enough credit.","detail":"You have 30 credits, but that costs 50.","instance":"https://example.net/account/12345/msgs/abc"}` | `"You do not have enough credit.\nYou have 30 credits, but that costs 50."` |
| `{"status":404,"title":"Not Found"}` | `Not Found` |
| `{"status":503}` | `503` |
| `{}` | `about:blank` |

### Operations

It also takes `text.format`, `json.read`, `json.write` and `==`. [How every standard type behaves](/docs/every-standard-type-meets-one-contract.md) says how they work.

</details>
