Manejo de Errores
Entiende las respuestas de error de la API y cómo manejarlas.
Formato de Respuesta de Error
Todas las respuestas de error siguen un formato consistente:
{
"error": {
"code": "ERROR_CODE",
"message": "Descripción legible por humanos",
"details": [
{
"field": "nombre_campo",
"message": "Mensaje de error específico"
}
]
}
}Códigos de Estado HTTP
| Código | Significado | Descripción |
|---|---|---|
200 | OK | Solicitud exitosa |
201 | Created | Recurso creado |
204 | No Content | Eliminación exitosa |
400 | Bad Request | Datos de solicitud inválidos |
401 | Unauthorized | Autenticación faltante o inválida |
403 | Forbidden | Permisos insuficientes |
404 | Not Found | Recurso no existe |
409 | Conflict | Conflicto de recurso (duplicado) |
422 | Unprocessable Entity | Error de validación |
429 | Too Many Requests | Límite de tasa excedido |
500 | Internal Server Error | Error del servidor |
Códigos de Error
Errores de Autenticación
| Código | Descripción |
|---|---|
UNAUTHORIZED | No se proporcionó token de autenticación |
INVALID_TOKEN | Token inválido o expirado |
INVALID_API_KEY | Clave API inválida |
API_KEY_EXPIRED | Clave API ha expirado |
{
"error": {
"code": "UNAUTHORIZED",
"message": "Se requiere autenticación"
}
}Errores de Autorización
| Código | Descripción |
|---|---|
FORBIDDEN | El usuario no tiene acceso |
INSUFFICIENT_PERMISSIONS | Permisos insuficientes |
ORGANIZATION_ACCESS_DENIED | No es miembro de la organización |
{
"error": {
"code": "INSUFFICIENT_PERMISSIONS",
"message": "Permiso insuficiente: se requiere components:write"
}
}Errores de Validación
| Código | Descripción |
|---|---|
VALIDATION_ERROR | Los datos de entrada fallaron validación |
INVALID_FORMAT | Formato de datos incorrecto |
MISSING_REQUIRED_FIELD | Falta campo requerido |
INVALID_VALUE | El valor de campo es inválido |
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Los datos de solicitud son inválidos",
"details": [
{
"field": "name",
"message": "El nombre es requerido"
},
{
"field": "url",
"message": "Debe ser una URL válida"
}
]
}
}Errores de Recurso
| Código | Descripción |
|---|---|
NOT_FOUND | Recurso no existe |
ALREADY_EXISTS | El recurso ya existe |
CONFLICT | Conflicto con estado actual |
RESOURCE_LOCKED | Recurso está bloqueado |
{
"error": {
"code": "NOT_FOUND",
"message": "Componente no encontrado",
"details": {
"resourceType": "Component",
"resourceId": "comp-123"
}
}
}Errores de Límite de Tasa
| Código | Descripción |
|---|---|
RATE_LIMIT_EXCEEDED | Demasiadas solicitudes |
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Límite de tasa excedido. Intenta de nuevo en 60 segundos.",
"details": {
"retryAfter": 60,
"limit": 100,
"remaining": 0
}
}
}Errores de Servidor
| Código | Descripción |
|---|---|
INTERNAL_ERROR | Error inesperado del servidor |
SERVICE_UNAVAILABLE | Servicio temporalmente no disponible |
DATABASE_ERROR | Error de operación de base de datos |
{
"error": {
"code": "INTERNAL_ERROR",
"message": "Ocurrió un error inesperado"
}
}Manejo de Errores
JavaScript/TypeScript
async function fetchComponents() {
const response = await fetch('/api/v1/components', {
headers: { 'Authorization': 'Bearer sk_live_xxx' }
});
if (!response.ok) {
const error = await response.json();
switch (response.status) {
case 401:
// Refrescar token o re-autenticar
throw new Error('Autenticación requerida');
case 403:
throw new Error(`Acceso denegado: ${error.error.message}`);
case 404:
return null; // Recurso no existe
case 422:
// Manejar errores de validación
const fields = error.error.details.map(d => d.field);
throw new Error(`Campos inválidos: ${fields.join(', ')}`);
case 429:
// Implementar backoff exponencial
const retryAfter = error.error.details.retryAfter;
await sleep(retryAfter * 1000);
return fetchComponents(); // Reintentar
default:
throw new Error(error.error.message);
}
}
return response.json();
}Python
import requests
import time
def fetch_components():
response = requests.get(
'https://tu-dominio.com/api/v1/components',
headers={'Authorization': 'Bearer sk_live_xxx'}
)
if response.status_code == 401:
raise Exception('Autenticación requerida')
if response.status_code == 403:
error = response.json()
raise Exception(f"Acceso denegado: {error['error']['message']}")
if response.status_code == 404:
return None
if response.status_code == 422:
error = response.json()
fields = [d['field'] for d in error['error']['details']]
raise Exception(f"Campos inválidos: {', '.join(fields)}")
if response.status_code == 429:
error = response.json()
retry_after = error['error']['details']['retryAfter']
time.sleep(retry_after)
return fetch_components() # Reintentar
response.raise_for_status()
return response.json()Mejores Prácticas
1. Siempre Verifica el Código de Estado
if (!response.ok) {
// Manejar error
}2. Implementa Reintentos con Backoff
async function fetchWithRetry(url: string, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url);
if (response.status === 429) {
const delay = Math.pow(2, i) * 1000; // Backoff exponencial
await sleep(delay);
continue;
}
return response;
}
throw new Error('Max reintentos alcanzado');
}3. Registra Errores para Debugging
try {
const data = await fetchComponents();
} catch (error) {
console.error('Error de API:', {
message: error.message,
code: error.code,
requestId: response.headers.get('x-request-id')
});
}Siempre incluye el header x-request-id al reportar errores al soporte.
4. Maneja Errores de Validación Elegantemente
if (error.code === 'VALIDATION_ERROR') {
// Muestra errores junto a los campos del formulario
error.details.forEach(detail => {
showFieldError(detail.field, detail.message);
});
}