# Erori

Codurile HTTP pe care le întoarce platforma, ce înseamnă fiecare și ce faci când îl primești.

## Coduri HTTP

| Cod | Înseamnă | Ce faci |
| --- | --- | --- |
| `200`, `201` | reușit; `201` când s-a creat ceva | — |
| `202` | primit, se procesează mai târziu (de exemplu cererea de resetare a parolei) | — |
| `400` | cererea nu trece de schemă sau nu se poate citi (JSON stricat, tip de conținut greșit) | corectezi câmpurile |
| `401` | nu ești autentificat sau ruta nu este declarată | intri în cont din nou |
| `403` | ești autentificat, dar nu ai rolul sau dreptul | ceri administratorului acces |
| `404` | obiectul nu există în firma activă | verifici identificatorul și firma |
| `409` | starea obiectului nu permite schimbarea (de exemplu un document emis) sau o cerere cu aceeași cheie de idempotență încă rulează | reîncarci obiectul și decizi din nou; la `idempotency_key_in_lucru`, aștepți și repeți |
| `413` | corpul cererii este prea mare | trimiți mai puțin |
| `422` | datele sunt corecte ca formă, dar regula de business le refuză | citești `eroare`; unele rute adaugă un câmp cu explicația (`motiv`, `detalii` sau lista elementelor refuzate) |
| `429` | ai trecut de limită | aștepți `Retry-After` secunde |
| `500` | eroare neprevăzută în platformă; corpul e mereu `{"eroare": "eroare interna"}`, fără detalii | reîncerci; dacă se repetă, trimiți administratorului antetul `x-request-id` al răspunsului |
| `502` | serviciul extern (FGO, ANAF, un canal) a răspuns cu eroare | reîncerci mai târziu |
| `503` | sursa externă nu este configurată sau lipsește | administratorul configurează sursa |

> [!NOTE]
> Un `404` poate însemna și că obiectul există, dar în altă firmă. Platforma nu spune niciodată dacă un obiect există într-o firmă la care nu ai acces.

## Codurile din câmpul `eroare`

Cele mai frecvente valori, numărate în cod:

| `eroare` | Cod HTTP obișnuit | Înseamnă |
| --- | --- | --- |
| `neautentificat` | `401` | lipsește sesiunea sau a expirat |
| `ruta_nedeclarata` | `401` | ruta nu are acces declarat |
| `interzis` | `403` | rolul sau dreptul lipsește |
| `negasit` | `404` | obiectul nu există în firma activă |
| `cerere_invalida` | `400` | parametri sau corp care nu trec de schemă |
| `corp_invalid` | `400` | corpul cererii nu trece de schemă |
| `intrare invalida` | `400` | schema rutei a respins cererea; câmpul `detalii` spune ce câmp și de ce |
| `cerere invalida` | `400` (sau alt `4xx`) | cererea nu s-a putut citi: JSON stricat, corp prea mare, tip de conținut greșit |
| `prea_multe_cereri` | `429` | limita unei rute speciale |
| `prea multe cereri` | `429` | limita generală a sesiunii sau a adresei IP, pentru scrieri și pentru citiri |
| `sursa_neconfigurata` | `503` | sursa externă nu este configurată |
| `parola_comuna` | `400` | parola apare în listele de parole sparte |
| `parola_anterioara` | `400` | parola este una dintre ultimele 5 ale contului |
| `jeton_invalid` | `400` | jeton expirat, revocat sau greșit |
| `idempotency_key_invalid` | `400` | antetul `Idempotency-Key` are caractere nepermise sau e prea lung ([detalii](#idempotenta)) |
| `idempotency_key_alt_corp` | `422` | aceeași cheie de idempotență, trimisă cu alt corp |
| `idempotency_key_in_lucru` | `409` | prima cerere cu aceeași cheie încă rulează |
| `eroare interna` | `500` | eroare neprevăzută; detaliile rămân doar în jurnalul serverului |

## Formatul RFC 9457, la cerere

Dacă trimiți antetul `Accept: application/problem+json`, orice eroare cu câmpul `eroare` vine în formatul [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457), cu `content-type: application/problem+json; charset=utf-8`. Fără antet (sau cu `q=0` pe acest tip), corpul rămâne cel de mai sus, neschimbat.

```bash title="Cererea formatului nou"
curl https://app.ilustromania.com/nucleu/v1/oferte/nu-exista \
  -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \
  -H "Accept: application/problem+json"
```

În formatul nou, toate câmpurile vechi rămân (inclusiv `eroare`, `detalii`, `motiv`), iar în plus apar:

| Câmp | Ce conține |
| --- | --- |
| `type` | `urn:ilustromania:eroare:<cod>` |
| `title` | titlul scurt al codului, în română |
| `status` | codul HTTP, ca număr |
| `cod` | codul canonic, stabil: după el compari în integrare |
| `id_cerere` | același număr ca în antetul `x-request-id` |
| `cod_detaliu` | doar când ruta trimitea deja un câmp `cod` (avize, emitere, oferte, tabelul de produse): valoarea lui veche |

```json title="Eroare în format RFC 9457"
{
  "eroare": "corp_invalid",
  "type": "urn:…:eroare:cerere_invalida",
  "title": "Cerere invalidă",
  "status": 400,
  "cod": "cerere_invalida",
  "id_cerere": "…"
}
```

## Codurile canonice

Câmpul `cod` adună mesajele vechi care spun același lucru. Un mesaj care nu e în tabel și arată deja ca un cod (litere mici, cifre și `_`, de exemplu `ruta_nedeclarata` sau `idempotency_key_alt_corp`) rămâne neschimbat; altfel codul vine din statusul HTTP.

| `cod` | `title` | Status implicit | Mesajele vechi adunate aici |
| --- | --- | --- | --- |
| `cerere_invalida` | Cerere invalidă | `400`, `422` | `cerere_invalida`, `corp_invalid`, `date_gresite`, `date_nevalide`, `intrare invalida`, `cerere invalida`, `parametri_invalizi`, `interogare_invalida`, `id_invalid` |
| `neautentificat` | Neautentificat | `401` | `neautentificat` |
| `interzis` | Interzis | `403` | `interzis` |
| `negasit` | Negăsit | `404` | `negasit` |
| `conflict` | Conflict | `409` | — |
| `prea_mare` | Prea mare | `413` | — |
| `prea_multe_cereri` | Prea multe cereri | `429` | `prea multe cereri`, `prea_multe_cereri`, `prea_multe_incercari`, `prea multe incercari`, `limita_depasita` |
| `eroare_interna` | Eroare internă | `500` | `eroare interna` |

Un status care nu e în tabel dă `eroare_interna` de la `500` în sus și `cerere_invalida` sub `500`. Pentru codurile care nu sunt în tabel, `title` este chiar codul.

> [!NOTE]
> Erorile OAuth, MCP și fluxurile în timp real (SSE) nu se transformă: au formatul lor, descris mai jos.

## Erorile OAuth

Rutele OAuth folosesc formatul din standard, nu câmpul `eroare`:

```json title="Eroare OAuth"
{ "error": "unsupported_grant_type", "error_description": "grant_type nesuportat" }
```

## Erorile MCP

Serverul MCP răspunde cu obiecte de eroare JSON-RPC 2.0 (`error.code`, `error.message`). O unealtă care eșuează întoarce rezultatul cu `isError: true`, ca asistentul să poată citi motivul.
