# Errori

> Un unico documento di problema leggibile dalle macchine per ogni errore, e cosa significa ciascun tipo per la sua logica di retry.

Source: https://developers.backresto.com/it/docs/errors

Ogni errore (compresa una rotta che non esiste) risponde con
`application/problem+json`, la forma definita dalla RFC 9457. Non esiste alcun
percorso in questa API che restituisca una pagina di errore HTML, quindi un
parser che dà per scontato il JSON su una risposta non 2xx è al sicuro.

```json
{
  "type": "https://api.backresto.com/problems/forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "The authenticated client lacks the required scope.",
  "instance": "/v1/restaurants/restaurant-1/collections/delivery-pictures/records/8f2c/assets",
  "requestId": "b1c8b0a4-6c1e-4f6b-9e1c-3b2a5d7f0e91"
}
```

`type` è l'identificatore stabile: basi la sua logica su quello, non su
`detail`, che è prosa e può essere riformulato. `requestId` è il valore da
citare in un messaggio al supporto. Viene restituito anche nell'header di
risposta `X-Request-ID`, e può impostare lei stesso quell'header per correlare
con le sue tracce.

## Il catalogo

| Suffisso di `type` | Stato | Cosa è successo | Ritentare? |
| --- | --- | --- | --- |
| `invalid-request` | 400 | Manca un parametro oppure è malformato: un `limit` fuori intervallo, una collezione sconosciuta, oppure un cursore che non corrisponde al ristorante, alla collezione e al `limit` con cui sta paginando. | No. Corregga la richiesta. |
| `unauthorized` | 401 | Nessun header `Authorization`, oppure una chiave malformata, sconosciuta, revocata, scaduta o rimasta senza alcuna concessione. | No. La chiave richiede attenzione. |
| `forbidden` | 403 | La chiave ha una concessione per questo ristorante ma non lo scope richiesto da questo endpoint. | No. Il cliente deve far emettere una chiave con quello scope. |
| `not-found` | 404 | Nessuna concessione per questo ristorante, oppure quel record non esiste. I casi sono deliberatamente indistinguibili. | No. |
| `conflict` | 409 | La richiesta è in conflitto con una risorsa esistente. | No. |
| `payload-too-large` | 413 | Il corpo della richiesta supera il limite. | No. |
| `rate-limit-exceeded` | 429 | Il suo client ha superato il proprio budget di richieste. | Sì, dopo `Retry-After`. |
| `dependency-unavailable` | 503 | Un componente necessario all'API è momentaneamente non disponibile. | Sì, con backoff. |
| `internal-error` | 500 | Un errore imprevisto, già registrato da parte nostra. | Una volta, con backoff; poi ci comunichi il `requestId`. |

## Cosa dovrebbe fare un client

Tre categorie, e nient'altro vale il codice che costa:

**Non ritentare**: 400, 401, 403, 404, 409, 413. La richiesta fallirà di nuovo
in modo identico. Registri il `type` e il `requestId`, e li renda visibili:
un `401` in particolare significa che una persona dalla parte del suo cliente ha
revocato qualcosa, e un ciclo di tentativi non farà che nasconderlo per una
settimana.

**Ritentare con backoff**: 429, 503. Rispetti `Retry-After` su un 429; è il
numero di secondi prima che il suo budget si ricarichi, e non è un suggerimento.
Per il 503, backoff esponenziale con jitter, e rinunci invece di martellare.

**Ritentare una volta, poi segnalare**: 500. Se si ripete sulla stessa
richiesta, non è transitorio.

Ogni endpoint di questa API è un `GET`, quindi ritentare è sempre sicuro dal
punto di vista dell'API; il motivo per non ritentare un 4xx è che spreca il suo
budget, non che rischia una doppia scrittura.

## Correlare con i suoi log

Invii un suo identificatore e le torna indietro:

```bash
curl "https://api.backresto.com/v1/restaurants/$RESTAURANT_ID/collections/deliveries/records?limit=100" \
  -H "Authorization: Bearer $BACKRESTO_PARTNER_API_KEY" \
  -H "X-Request-ID: 3f7a1c9e-2b4d-4a86-9f0c-5e1d8b6a2c40" \
  --include
```

Il valore viene riproposto nell'header di risposta e nel `requestId` di un
documento di problema, il che fa sì che un solo grep risponda a «cosa ha visto
BackResto quando il mio job è fallito alle 04:12?».
