Español
Referencia API
Errores

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ódigoSignificadoDescripción
200OKSolicitud exitosa
201CreatedRecurso creado
204No ContentEliminación exitosa
400Bad RequestDatos de solicitud inválidos
401UnauthorizedAutenticación faltante o inválida
403ForbiddenPermisos insuficientes
404Not FoundRecurso no existe
409ConflictConflicto de recurso (duplicado)
422Unprocessable EntityError de validación
429Too Many RequestsLímite de tasa excedido
500Internal Server ErrorError del servidor

Códigos de Error

Errores de Autenticación

CódigoDescripción
UNAUTHORIZEDNo se proporcionó token de autenticación
INVALID_TOKENToken inválido o expirado
INVALID_API_KEYClave API inválida
API_KEY_EXPIREDClave API ha expirado
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Se requiere autenticación"
  }
}

Errores de Autorización

CódigoDescripción
FORBIDDENEl usuario no tiene acceso
INSUFFICIENT_PERMISSIONSPermisos insuficientes
ORGANIZATION_ACCESS_DENIEDNo es miembro de la organización
{
  "error": {
    "code": "INSUFFICIENT_PERMISSIONS",
    "message": "Permiso insuficiente: se requiere components:write"
  }
}

Errores de Validación

CódigoDescripción
VALIDATION_ERRORLos datos de entrada fallaron validación
INVALID_FORMATFormato de datos incorrecto
MISSING_REQUIRED_FIELDFalta campo requerido
INVALID_VALUEEl 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ódigoDescripción
NOT_FOUNDRecurso no existe
ALREADY_EXISTSEl recurso ya existe
CONFLICTConflicto con estado actual
RESOURCE_LOCKEDRecurso está bloqueado
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Componente no encontrado",
    "details": {
      "resourceType": "Component",
      "resourceId": "comp-123"
    }
  }
}

Errores de Límite de Tasa

CódigoDescripción
RATE_LIMIT_EXCEEDEDDemasiadas 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ódigoDescripción
INTERNAL_ERRORError inesperado del servidor
SERVICE_UNAVAILABLEServicio temporalmente no disponible
DATABASE_ERRORError 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);
  });
}

Documentación Relacionada