# Errores

> La forma de los errores, cada código de error y cómo reintentar de forma segura.

Los errores usan códigos HTTP normales y un cuerpo JSON con un `code` estable que puede comprobar en su código, y un `message` escrito para personas.

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

`issues` solo está allí para `validation_error`. Cada problema tiene `path` que apunta al campo, con posiciones de lista contadas desde 0.

## Códigos de error

| Estado | Código                | Qué ocurrió                                                                                                               | Qué hacer                                                                                     |
| ------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| 400    | `validation_error`    | Un parámetro de consulta o campo del cuerpo está faltando o es inválido.                                                  | Corrija los campos listados en `issues`.                                                      |
| 400    | `invalid_json`        | El cuerpo no es JSON válido.                                                                                              | Envíe JSON con `Content-Type: application/json`.                                              |
| 400    | `invalid_request`     | La solicitud es válida pero no está permitida en este momento, por ejemplo, marcar un nuevo pedido como completado.       | Lea `message`; no intente de nuevo sin cambios.                                               |
| 401    | `unauthorized`        | Clave de API faltante, desconocida o revocada.                                                                            | Compruebe el encabezado `Authorization: Bearer` y que la clave aún esté activa.               |
| 401    | `key_owner_removed`   | La persona que creó la clave ya no está en el espacio de trabajo.                                                         | Cree una nueva clave desde alguien que aún tenga acceso.                                      |
| 403    | `insufficient_scope`  | Una clave de solo lectura intentó escribir.                                                                               | Use una clave de lectura y escritura.                                                         |
| 403    | `read_only`           | Las escrituras de API no están disponibles en el plan Free actual del espacio de trabajo.                                 | El propietario gestiona el plan en el sitio web. Las claves existentes aún leen.              |
| 403    | `forbidden`           | La persona que creó la clave no tiene permiso para hacer eso.                                                             | Pida a un propietario del espacio de trabajo, o cree una clave desde alguien con ese permiso. |
| 403    | `workspace_suspended` | TradeCatalog ha suspendido el espacio de trabajo. El propietario ha recibido un correo electrónico con la razón.          | Las claves funcionan nuevamente cuando se levanta la suspensión.                              |
| 404    | `not_found`           | No existe ese punto de conexión, o no existe ese producto, cliente, pedido o cambio de precio en este espacio de trabajo. | Verifique el id, SKU o código de cuenta.                                                      |
| 405    | `method_not_allowed`  | La ruta existe pero no con este método.                                                                                   | Use un método del encabezado `Allow`.                                                         |
| 429    | `rate_limited`        | Más de 120 solicitudes por minuto con esta clave.                                                                         | Espere `Retry-After` segundos (60), luego intente de nuevo.                                   |
| 500    | `internal_error`      | Algo salió mal por nuestra parte.                                                                                         | Vuelva a intentarlo con backoff. Si sigue ocurriendo, contacte al soporte.                    |

Los IDs de otro espacio de trabajo siempre devuelven `404 not_found`, nunca datos de otra persona.

## Reintento

Vuelva a intentar las respuestas `429` y `5xx` con backoff. No intente nuevamente otras respuestas `4xx`: fallarán de la misma manera.

Todos los escritos de la API son seguros para repetir: `PUT` coincide filas por SKU o código de cuenta, y `PATCH /orders/{id}` falla con `invalid_request` si el pedido ya ha avanzado. Por lo tanto, un reintento después de un tiempo de espera de red no duplicará nada.

```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
}
```

## Mensajes comunes

| Solicitud                                     | Estado y mensaje                                             |
| --------------------------------------------- | ------------------------------------------------------------ |
| `GET /prices` con un SKU desconocido          | `404` “No hay producto con SKU HX-9999”                      |
| Cualquier endpoint con un cliente desconocido | `404` “Cliente OAK99 no encontrado”                          |
| `PATCH /orders/{id}` nuevo → completado       | `400` “Un pedido recibido no puede marcarse como completado” |
| `PATCH /orders/{id}` a cancelado sin nota     | `400` “Proporcione un motivo para la cancelación”            |
| `PUT /products` con el mismo SKU dos veces    | `400` error de validación “SKU duplicado hx-8510”            |
