> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fhiron.cl/llms.txt
> Use this file to discover all available pages before exploring further.

# Manejo de errores

> Jerarquía de errores tipados del SDK de Fhiron: auth, cuota, request, servidor, timeout y red.

`validate()` lanza errores tipados ante fallos HTTP o de red (un recurso inválido **no** lanza: se devuelve en `result.errors`). Todos extienden `FhironError`, así que puedes capturar cualquier fallo del SDK con un solo `instanceof` y luego distinguir el caso.

```ts theme={null}
import { Fhiron, FhironError, FhironQuotaError, FhironAuthError } from '@fhiron/sdk';

try {
  await fhiron.validate(resource);
} catch (err) {
  if (err instanceof FhironQuotaError) {
    notificarUpgrade(err.upgradeUrl);
  } else if (err instanceof FhironAuthError) {
    pedirNuevaApiKey();
  } else if (err instanceof FhironError) {
    log.error('Fallo de Fhiron:', err.message);
  } else {
    throw err; // no es del SDK
  }
}
```

## Jerarquía

| Clase                | HTTP            | Cuándo                                           | Reintentable      |
| -------------------- | --------------- | ------------------------------------------------ | ----------------- |
| `FhironAuthError`    | 401 / 403       | API key ausente, inválida o sin permisos.        | No                |
| `FhironQuotaError`   | 429             | Cuota mensual agotada. Expone `.upgradeUrl`.     | No (sube de plan) |
| `FhironRequestError` | 400 / 415 / 422 | Problema de forma del recurso o la petición.     | No                |
| `FhironServerError`  | 5xx             | Servidor o motor de validación caído.            | Sí                |
| `FhironTimeoutError` | n/a             | La petición superó `timeout`. Expone `.timeout`. | Sí                |
| `FhironNetworkError` | n/a             | Fallo de red, DNS, offline.                      | Sí                |

Todas exponen `.status` (cuando hay respuesta HTTP) y `.body` (el cuerpo crudo del error).

## Patrón de reintento

Los errores marcados como reintentables (`FhironServerError`, `FhironTimeoutError`, `FhironNetworkError`) admiten un backoff simple:

```ts theme={null}
import { FhironServerError, FhironTimeoutError, FhironNetworkError } from '@fhiron/sdk';

const RETRYABLE = [FhironServerError, FhironTimeoutError, FhironNetworkError];

async function validarConReintento(fhiron, resource, intentos = 3) {
  for (let i = 0; i < intentos; i++) {
    try {
      return await fhiron.validate(resource);
    } catch (err) {
      const esReintentable = RETRYABLE.some((C) => err instanceof C);
      if (!esReintentable || i === intentos - 1) throw err;
      await new Promise((r) => setTimeout(r, 500 * 2 ** i));
    }
  }
}
```

No reintentes ante `FhironAuthError`, `FhironRequestError` ni `FhironQuotaError`: el resultado no cambiará sin corregir la key, el recurso o el plan.

## Recurso inválido no es un error

Importante: que un recurso **no cumpla** CL Core no lanza una excepción. La llamada resuelve con `result.ok === false` y los hallazgos en `result.errors`. Las excepciones son solo para fallos de transporte o autorización.
