# Erreurs

> La structure des erreurs, chaque code d'erreur et comment réessayer en toute sécurité.

Les erreurs utilisent les codes d'état HTTP normaux et un corps JSON avec un `code` stable que vous pouvez vérifier dans votre code, et un `message` écrit pour les personnes.

```json
{
  "error": {
    "code": "validation_error",
    "message": "Some fields are invalid.",
    "issues": [
      {
        "path": "products.0.listPrice",
        "message": "Too small: expected number to be >=0"
      }
    ]
  }
}
```

`issues` n'est là que pour `validation_error`. Chaque problème a un `path` qui pointe sur le champ, avec les positions de liste comptées à partir de 0.

## Codes d'erreur

| Statut | Code                  | Ce qui s'est passé                                                                                                  | Que faire                                                                                                      |
| ------ | --------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| 400    | `validation_error`    | Un paramètre de requête ou un champ du corps est manquant ou invalide.                                              | Corrigez les champs listés dans `issues`.                                                                      |
| 400    | `invalid_json`        | Le corps n'est pas un JSON valide.                                                                                  | Envoyez un JSON avec `Content-Type: application/json`.                                                         |
| 400    | `invalid_request`     | La requête est valide mais non autorisée pour le moment, par exemple, marquer une nouvelle commande comme terminée. | Lisez `message` ; ne réessayez pas sans modification.                                                          |
| 401    | `unauthorized`        | Clé API manquante, inconnue ou révoquée.                                                                            | Vérifiez l'en-tête `Authorization: Bearer` et que la clé est toujours active.                                  |
| 401    | `key_owner_removed`   | La personne ayant créé la clé a quitté l'espace de travail.                                                         | Créez une nouvelle clé à partir de quelqu'un qui a encore accès.                                               |
| 403    | `insufficient_scope`  | Une clé en lecture seule a tenté d'écrire.                                                                          | Utilisez une clé Lecture & écriture.                                                                           |
| 403    | `read_only`           | Les écritures API sont indisponibles sur l'abonnement Free actuel de l'espace de travail.                           | Le propriétaire gère le plan sur le site web. Les clés existantes peuvent toujours lire.                       |
| 403    | `forbidden`           | La personne ayant créé la clé n'est pas autorisée à faire cela.                                                     | Demandez à un propriétaire d'espace de travail, ou créez une clé à partir de quelqu'un ayant cette permission. |
| 403    | `workspace_suspended` | TradeCatalog a mis en quarantaine l'espace de travail. Le propriétaire a reçu un courriel expliquant pourquoi.      | Les clés fonctionnent à nouveau lorsque la quarantaine est levée.                                              |
| 404    | `not_found`           | Aucun point de terminaison, produit, client, commande ou changement de prix dans cet espace de travail.             | Vérifiez l'id, le SKU ou le code de compte.                                                                    |
| 405    | `method_not_allowed`  | Le chemin existe mais pas avec cette méthode.                                                                       | Utilisez une méthode de l'en-tête `Allow`.                                                                     |
| 429    | `rate_limited`        | Plus de 120 requêtes par minute avec cette clé.                                                                     | Attendez `Retry-After` secondes (60), puis réessayez.                                                          |
| 500    | `internal_error`      | Quelque chose s'est mal passé de notre côté.                                                                        | Réessayez avec backoff. Si cela persiste, contactez le support.                                                |

Les identifiants d’un autre espace de travail donnent toujours `404 not_found`, jamais les données d’un autre utilisateur.

## Réessayer

Réessayez les réponses `429` et `5xx` avec un délai exponentiel. Ne réessayez pas les autres réponses `4xx` : elles échoueront de la même manière.

Toutes les écritures API sont sécurisées pour être répétées : `PUT` correspond aux lignes par SKU ou code de compte, et `PATCH /orders/{id}` échoue avec `invalid_request` si la commande a déjà avancé. Ainsi, un réessai après un timeout réseau ne dupliquera rien.

```js
async function api(path, init = {}, attempt = 0) {
  const res = await fetch(`https://tradecatalog.app/api/v1${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}`,
      'Content-Type': 'application/json',
      ...init.headers,
    },
  })
  if ((res.status === 429 || res.status >= 500) && attempt < 5) {
    const wait = Number(res.headers.get('Retry-After')) || 2 ** attempt
    await new Promise((resolve) => setTimeout(resolve, wait * 1000))
    return api(path, init, attempt + 1)
  }
  const body = await res.json()
  if (!res.ok) {
    const error = new Error(`${body.error.code}: ${body.error.message}`)
    error.status = res.status
    error.code = body.error.code
    error.issues = body.error.issues
    throw error
  }
  return body
}
```

## Messages courants

| Demande                                    | Statut et message                                                    |
| ------------------------------------------ | -------------------------------------------------------------------- |
| `GET /prices` avec un SKU inconnu          | `404` « Aucun produit avec le SKU HX-9999 »                          |
| Toute finition avec un client inconnu      | `404` « Client OAK99 non trouvé »                                    |
| `PATCH /orders/{id}` nouveau → terminé     | `400` « Une commande reçue ne peut pas être marquée comme terminée » |
| `PATCH /orders/{id}` annulé sans note      | `400` « Donnez une raison pour l'annulation »                        |
| `PUT /products` avec le même SKU deux fois | `400` erreur de validation « SKU dupliqué hx-8510 »                  |
