El sobre de error
La forma exacta de un error, los nueve tipos, qué hacer con cada uno y por qué el request_id importa.
Un error es un código HTTP más un solo objeto error. No hay sobre de éxito: las respuestas exitosas
son el recurso.
{
"error": {
"type": "invalid_request_error",
"code": "receptor_incomplete",
"message": "Receptor 76543212-K is not in the SII contribuyente registry and no giro was supplied. Provide receptor.giro and receptor.address.",
"param": "receptor.giro",
"doc_url": "https://docs.facturia.cl/errors/receptor_incomplete",
"request_id": "req_01K2R7Q9M4T2VB",
"empresa": "77928532-4"
}
}| Campo | Qué es |
|---|---|
type | La familia del error. Determina qué hacer |
code | El error específico, estable. Programa contra esto |
message | Prosa legible por una persona. No programes contra esto |
param | El campo del request que causó el problema, cuando aplica |
doc_url | Un enlace a la explicación de este código |
request_id | El identificador de esta llamada exacta |
empresa | El RUT en cuyo contexto ocurrió |
existing_id | En conflictos, el id del objeto que ya existe |
sii | Presente en sii_error — el estado crudo del SII |
Los nueve tipos
type | HTTP | Significa | Qué hacer |
|---|---|---|---|
invalid_request_error | 400 | Entrada mal formada o incompleta | Arregla el request. Reintentar igual no sirve |
authentication_error | 401 | Llave ausente, revocada o mal formada | Revisa la credencial |
permission_error | 403 | Llave válida, no permitida: scope, lista blanca de empresas, modo | Revisa el scope o la empresa. error.message nombra el que falta |
not_found_error | 404 | No existe ese objeto bajo esta llave | Revisa el id y el modo de la llave |
conflict_error | 409 | Conflicto de estado: reuso de idempotencia, folio duplicado, empresa ya existente | Mira existing_id |
unprocessable_error | 422 | Bien formado pero viola una regla del dominio: folios agotados, ventana de acuse cerrada, empresa que aún no emite | Es una regla de negocio, no un bug de formato |
rate_limit_error | 429 | Más lento; mira Retry-After | Espera lo que dice el header |
sii_error | 502 | El SII rechazó o falló la llamada de arriba. error.sii trae el estado y la glosa crudos | Ver Códigos |
api_error | 500 / 503 | Nuestro. Nada se emitió | Reintenta con backoff |
402 plan_limit_reached aparece cuando se agota una cuota de plan; el cuerpo nombra el límite y la
URL para subir. El modo test nunca tiene límite por plan.
El request_id
Loguéalo siempre
request_id aparece en cada respuesta, exitosa o no, en el header Facturia-Request-Id. Con él
podemos encontrar la llamada exacta. Sin él, una conversación de soporte empieza por reconstruir qué
pasó.
Un cliente HTTP bien hecho lo guarda junto al resultado, no solo cuando algo falla.
Cómo manejar errores en la práctica
async function llamar(path, init) {
const res = await fetch(`https://api.facturia.cl/v1${path}`, init);
const requestId = res.headers.get('Facturia-Request-Id');
if (res.ok) return { data: await res.json(), requestId };
const { error } = await res.json();
switch (error.type) {
case 'rate_limit_error':
// El header dice cuánto. No adivines.
return { retryAfter: Number(res.headers.get('Retry-After') ?? 1), requestId };
case 'api_error':
// Nada se emitió. Reintenta con la MISMA Idempotency-Key.
throw new Reintentable(error, requestId);
case 'sii_error':
// Puede ser transitorio. `error.sii` dice qué respondió el SII.
throw new Reintentable(error, requestId);
case 'invalid_request_error':
case 'unprocessable_error':
// Reintentar el mismo cuerpo va a fallar igual. Arréglalo o escálalo.
throw new NoReintentable(error, requestId);
default:
throw new NoReintentable(error, requestId);
}
}Trampa
Un 4xx no quema tu Idempotency-Key; un 5xx sí es seguro de reintentar
Los requests que fallan con 4xx no consumen la llave de idempotencia: arregla el cuerpo y reintenta con la misma llave. Los fallos 5xx también son seguros de reintentar con la misma llave — esa es exactamente la situación para la que existe.
Lo que no debes hacer es generar una llave nueva al reintentar un timeout: ahí es donde nacen las facturas duplicadas.
El diccionario legible por máquina
curl https://api.facturia.cl/v1/error_codesSin autenticación. Devuelve cada código de Facturia, su contraparte del SII y si conviene reintentar. Es el mismo contenido que Códigos de rechazo, en una forma que tu build puede consumir.