Códigos de error API y mejores prácticas
1) Por qué estandarizar errores
Previsibilidad para los clientes: formato único y comportamiento retraído.
Aceleración de debag: 'trace _ id '/' request _ id', estable 'error _ code'.
Seguridad: SQL/stack traces/configuraciones no se filtrarán.
Observabilidad: Reporting sobre la taxonomía de errores (validación, cuotas, timeouts, etc.).
2) Principios básicos
1. Un formato de respuesta único para todos los 4xx/5xx (y para los 2xx con errores parciales es un esquema separado).
2. Clara semántica HTTP: el estado fiel es lo más importante.
3. Dos niveles de código: transporte ('status') y dominio estable 'error _ code'.
4. Retriable vs No Retriable: especifique explícitamente y vamos a dar una pista sobre el back-off.
5. Seguridad predeterminada: detalles - sólo al cliente con derechos; sin vías interiores.
6. Localización: el código de la máquina permanece estable, el texto es traducible.
3) Formato de error único (basado en RFC 7807)
JSON recomendado (avanzado 'application/problem + json'):json
{
"type": "https://api. example. com/errors/validation_failed",
"title": "Validation failed",
"status": 422,
"error_code": "VAL_001",
"detail": "Field 'email' must be a valid address",
"instance": "req_01HZY...93",
"trace_id": "a1b2c3d4e5f6",
"retriable": false,
"errors": [
{"field": "email", "code": "email_invalid", "message": "Invalid email"}
],
"hint": "Fix payload and retry",
"meta": {"docs": "https://docs. example. com/errors#VAL_001"}
}
Obligatorios: 'type', 'title', 'status', 'error _ code', 'trace _ id'.
Opcional: 'errors []' (por campos), 'retriable', 'hint', 'meta'.
- `Content-Type: application/problem+json`
- `X-Request-ID`/`Traceparent` (W3C)
- (para 429/503) 'Retry-After' (segundos o fecha)
4) Semántica de estados HTTP (fusión de «clásicos» y prácticas)
2xx (éxito con matices)
200 OK es un éxito común.
201 Creado: se ha creado un recurso (Location).
202 Aceptados - asíncronos en la cola (dar 'status _ url').
207 Multi-Status - Éxito parcial (evitar si se puede).
4xx (error del cliente)
400 Bad Request - sintaxis/formato, pero no validación de campos (mejor que 422).
401 Unauthorized - No/token incorrecto. Vamos 'WWW-Authenticate'.
403 Forbidden es un token válido, pero no hay suficientes derechos (RBAC/ABAC/límites).
404 Not Found - No hay recurso/endpoint.
409 Conflict es un conflicto de versiones/estado (locking optimistic, idempotency).
410 Gone - Endpoint permanentemente eliminado.
412 Precondición Fallida - ETag/If-Match no ha pasado.
415 Unsupported Media Type - 'Content-Type' incorrecto.
422 Unprocessable Entity - Validación de reglas de negocio.
429 Too Many Requests - Exceso de cuotas/velocidad (ver § 7).
5xx (error del servidor)
500 Error de servidor interno: error repentino; no revelar los detalles.
502 Bad Gateway - Error de upstream.
503 Servicio Unavailable - degradación/sobrecarga, dar 'Retry-After'.
504 Gateway Timeout - Timaut backend.
5) Taxonomía de dominios 'error _ code'
Recomendamos rangos:- 'AUTH _' - Autenticación/autorización.
- 'VAL _' es la validación de los datos de entrada.
- 'RATELIMIT _' - Cuotas y velocidad.
- 'IDEMP _' - idempotencia/duplicados.
- 'CONFLICT _' - Versiones/Estado.
- 'AMB _' - dependencias (PSP/DNS/SMTP).
- 'PAY _' es un error comercial de dominio de pago.
- 'SEC _' - seguridad (firmas, HMAC, mTLS).
- 'INT _' es un súbito interno.
- Estabilidad en el tiempo (back-compat).
- Descripciones y ejemplos en el directorio de errores (docs + machine-readable JSON).
6) Retriable vs Non-retriable
Campos:- `retriable: true|false`
- Si 'true' es necesariamente un 'Retry-After' (en segundos) o un contrato «back-off exponencial (a partir del 1-2 s, max 30-60 s)».
Retriable generalmente: '502/503/504', algunos '500', '429' (después de la ventana).
Non-retriable: `400/401/403/404/409/410/415/422`.
7) Puntuación limit & quota errores (429)
Cuerpo:json
{
"type": "https://api. example. com/errors/rate_limited",
"title": "Rate limit exceeded",
"status": 429,
"error_code": "RATELIMIT_RPS",
"detail": "Too many requests",
"retriable": true
}
Encabezados:
- `Retry-After: 12`
- `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`
- Для квот: `X-Quota-Limit`, `X-Quota-Remaining`, `X-Quota-Reset`
8) Idempotencia y conflictos
En las solicitudes de grabación, 'Idempotency-Key' (único dentro de 24-72 h).
Conflicto de re-operación → 409 Conflict con 'error _ code: "IDEMP_REPLAY"'.
Conflicto de versiones de recursos por ETag → 412 Precondition Failed.
En la respuesta, adjunte 'resource _ id '/' status _ url' para volver a solicitar de forma segura.
9) Validación y 422
Devuelva una lista de errores por campo:json
{
"status": 422,
"error_code": "VAL_001",
"errors": [
{"field":"email","code":"email_invalid","message":"Invalid email"},
{"field":"age","code":"min","message":"Must be >= 18"}
]
}
Reglas:
- No duplique lo mismo en 400 - 422 es preferible para la validación de negocios.
- Los mensajes son humanizados; 'code' es una máquina.
10) Seguridad de errores
Nunca: stack traces, SQL, rutas de archivos, nombres de host privados.
Edite PII; siga el GDPR/DSAR.
Para la firma/NMAS: Discernir 'SEC _ SIGNATURE _ MISMATCH' (403) y 'SEC _ TIMESTAMP _ SKEW' (401/403) con la sugerencia «compruebe el tiempo ± 5 min».
11) Correlación y observabilidad
Agregue siempre 'trace _ id '/' X-Request-ID' y busque en los logs/tracks.
Los errores se agregan por 'error _ code' y 'status' → los dashboards 'top bugs',' new vs known '.
Alertas: ráfaga 5xx/422/429, latencia p95, share of errors.
12) gRPC/GraphQL/Webhooks - mappings
gRPC ↔ HTTP
GraphQL
Transporte 200, pero 'errors []' dentro - añadir 'extensiones. code` и `trace_id`.
Para un «fatal» (autenticación/cuota) - mejor HTTP real 401/403/429.
Webhooks
Considere que sólo el destinatario 2xx tiene éxito.
Retrés con un respaldo exponencial, 'X-Webhook-ID', 'X-Signature'.
410 del destinatario - detener el retiro (endpoint quitado).
13) Versificación de errores
'type '/' error _ code' - estable; nuevos - sólo añadir.
Si cambia el esquema del cuerpo: aumente la versión menor de la API o 'problem + json; v=2`.
Documentación: tabla de códigos + ejemplos; changelog errores.
14) Documentación (fragmentos OpenAPI)
Respuestas globales
yaml components:
responses:
Problem:
description: Problem Details content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
schemas:
Problem:
type: object required: [type, title, status, error_code, trace_id]
properties:
type: { type: string, format: uri }
title: { type: string }
status: { type: integer }
error_code: { type: string }
detail: { type: string }
instance: { type: string }
trace_id: { type: string }
retriable: { type: boolean }
errors:
type: array items:
type: object properties:
field: { type: string }
code: { type: string }
message: { type: string }
Ejemplo de endpoint
yaml paths:
/v1/users:
post:
responses:
'201': { description: Created }
'401': { $ref: '#/components/responses/Problem' }
'422': { $ref: '#/components/responses/Problem' }
'429': { $ref: '#/components/responses/Problem' }
'500': { $ref: '#/components/responses/Problem' }
15) Pruebas y calidad
Pruebas de contrato: cumplimiento de 'application/problem + json', campos obligatorios.
Pruebas negativas: todas las ramas 401/403/404/ 409/422/429/500.
Chaos/latency: verificación de retraídas en 5xx/ 503/504/429 ('Retry-After').
Pruebas de seguridad: sin mensajes internos, máscara PII correcta.
Backward-compat: los antiguos clientes entienden los nuevos campos (añadir, no romper).
16) Lista de verificación de implementación
- Un solo 'problema + json' + estable 'error _ code'.
- Correcta semántica HTTP/gRPC/GraphQL.
- Retriable/no retriable + 'Retry-After '/recomendaciones de back-off.
- Rate-limit encabezados y 429 comportamientos.
- Idempotencia ('Idempotency-Key', 409/412).
- Seguridad: sin stack traces/secretos, edición PII.
- 'trace _ id '/' X-Request-ID' en todos los errores.
- Documentación del directorio de errores y ejemplos.
- Monitoreo de la taxonomía de errores.
- Autotestas de escenarios negativos.
17) Mini preguntas frecuentes
¿En qué se diferencia 400 de 422?
400 es una consulta rota (sintaxis/tipo de contenido). 422 es válido por sintaxis, pero las reglas de negocio no han pasado.
¿Cuándo 401 y cuándo 403?
401 - no/token incorrecto; 403 - el token está ahí, los derechos no son suficientes.
¿Es necesario siempre 'Retry-After'?
429/503 - Sí; para el resto retriable - es aconsejable hacer una recomendación explícita.
Resultado
Los errores bien diseñados son el contrato: estado HTTP correcto, 'problema + json' único, 'error _ code' estable, pistas explícitas de retrés y seguridad estricta. Estandarice el formato, documente la taxonomía, agregue telemetría y pruebas, y su API se volverá predecible, segura y amigable con los integradores.