Códigos de error
Toda respuesta de error usa la envoltura { error: { code, message, requestId, details? } }. La columna code es estable; usa eso para branching, no el mensaje.
Tabla maestra
| HTTP | code | Cuándo |
|---|---|---|
| 400 | INVALID_INPUT | JSON malformado, slug malformado, falta un campo requerido (zod), multipart sin file, etc. |
| 401 | UNAUTHORIZED | Falta header x-api-key o el valor no coincide. |
| 404 | TEMPLATE_NOT_FOUND | GET/PUT/DELETE/POST /render con slug que no existe. |
| 404 | NOT_FOUND | Ruta HTTP que no existe (catch-all). |
| 409 | TEMPLATE_CONFLICT | POST /templates con un slug que ya está registrado. Usa PUT para reemplazar. |
| 413 | PAYLOAD_TOO_LARGE | Upload de plantilla mayor a MAX_UPLOAD_MB; o body JSON mayor a 1 MB. |
| 422 | INVALID_DATA | El data de POST /render no cumple el JSON Schema de la plantilla. details.errors trae los errores ajv. |
| 500 | RENDER_ERROR | docxtemplater falló: sintaxis de plantilla rota, etiqueta de imagen fuera de tabla 1×1, etc. |
| 500 | CONVERSION_ERROR | LibreOffice falló al convertir a PDF (timeout, archivo dañado, fuente faltante). |
| 500 | INTERNAL_ERROR | Error no tipificado. Revisa logs con el requestId. |
| 502 | IMAGE_FETCH_ERROR | Imagen URL no alcanzable, content-type no-imagen, tamaño excedido, o IP bloqueada por SSRF guard. |
| 503 | LIBREOFFICE_UNAVAILABLE | format=pdf y LibreOffice no está instalado o no responde. |
Diagnóstico
Cada respuesta incluye requestId. Para encontrar el log:
# Si los logs van a stdout (NODE_ENV=production):
docker compose logs converter-api | grep "<requestId>"
# En desarrollo (pino-pretty):
# El reqId aparece en cada línea de log; los errores 5xx se loggean con stack traceDetalles ajv (422 INVALID_DATA)
El campo details.errors es el array crudo de ajv:
{
"error": {
"code": "INVALID_DATA",
"details": {
"slug": "factura-demo",
"errors": [
{
"instancePath": "/items/0",
"schemaPath": "#/properties/items/items/required",
"keyword": "required",
"params": { "missingProperty": "cantidad" },
"message": "must have required property 'cantidad'"
}
]
}
}
}Para mostrar errores legibles al usuario final: lee error.details.errors[].message.
Detalles zod (400 INVALID_INPUT)
Para errores de validación del body o params del API:
{
"error": {
"code": "INVALID_INPUT",
"details": {
"issues": [
{ "path": ["slug"], "message": "Invalid slug format" }
]
}
}
}