Códigos de Estado 4xx de Error del Cliente

Fundamentals Jan 9, 2026 HTTP
http status-codes errors client-errors rest

Definición

Los códigos de estado 4xx de Error del Cliente indican que la solicitud enviada por el cliente contiene un error o no puede cumplirse. El error está del lado del cliente (sintaxis incorrecta, datos inválidos, autenticación faltante, etc.).

Los códigos de estado 4xx más comunes son:

  • 400 Bad Request - Sintaxis de solicitud malformada o datos inválidos
  • 401 Unauthorized - Credenciales de autenticación faltantes o inválidas
  • 403 Forbidden - Autenticado pero carece de permiso para acceder al recurso
  • 404 Not Found - El recurso no existe en la URI especificada
  • 409 Conflict - La solicitud entra en conflicto con el estado actual (ej. email duplicado)
  • 422 Unprocessable Entity - Errores de validación en los datos de la solicitud
  • 429 Too Many Requests - Límite de tasa excedido

Los códigos 4xx están definidos en RFC 7231 y RFC 6585, indicando que el cliente debe modificar la solicitud antes de reintentar.

Ejemplo

400 Bad Request - JSON Inválido:

POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "name": "Alice",
  "email": "not-an-email"  // Formato de email inválido
}

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": "Validación fallida",
  "details": [
    {
      "field": "email",
      "message": "Formato de email inválido"
    }
  ]
}

401 Unauthorized - Token Faltante:

GET /api/users/me HTTP/1.1
Host: api.example.com

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api.example.com"
Content-Type: application/json

{
  "error": "Autenticación requerida",
  "message": "Token de acceso faltante o inválido"
}

403 Forbidden - Permisos Insuficientes:

DELETE /api/users/123 HTTP/1.1
Host: api.example.com
Authorization: Bearer YOUR_TOKEN

HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "error": "Prohibido",
  "message": "No tienes permiso para eliminar este usuario"
}

404 Not Found - El Recurso No Existe:

GET /api/users/999 HTTP/1.1
Host: api.example.com

HTTP/1.1 404 Not Found
Content-Type: application/json

{
  "error": "No encontrado",
  "message": "El usuario con ID 999 no existe"
}

409 Conflict - Recurso Duplicado:

POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "name": "Alice",
  "email": "[email protected]"  // El email ya existe
}

HTTP/1.1 409 Conflict
Content-Type: application/json

{
  "error": "Conflicto",
  "message": "Ya existe un usuario con el email [email protected]"
}

422 Unprocessable Entity - Error de Validación:

POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "name": "A",  // Muy corto
  "email": "[email protected]",
  "age": -5  // Edad inválida
}

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
  "error": "Validación fallida",
  "details": [
    {
      "field": "name",
      "message": "El nombre debe tener al menos 2 caracteres"
    },
    {
      "field": "age",
      "message": "La edad debe ser un número positivo"
    }
  ]
}

Ejemplo de Código

JavaScript (Fetch API):

const createUser = async (userData) => {
  try {
    const response = await fetch('https://api.example.com/users', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer YOUR_TOKEN'
      },
      body: JSON.stringify(userData)
    });

    // Manejar errores 4xx específicos
    if (response.status === 400) {
      const error = await response.json();
      console.error('Bad Request:', error);
      throw new Error(`Validación fallida: ${JSON.stringify(error.details)}`);
    }

    if (response.status === 401) {
      console.error('No autorizado - token expirado o faltante');
      // Redirigir al login
      window.location.href = '/login';
      return;
    }

    if (response.status === 403) {
      const error = await response.json();
      console.error('Prohibido:', error.message);
      throw new Error('Permisos insuficientes');
    }

    if (response.status === 404) {
      console.error('Recurso no encontrado');
      throw new Error('Usuario no encontrado');
    }

    if (response.status === 409) {
      const error = await response.json();
      console.error('Conflicto:', error.message);
      throw new Error('El usuario ya existe');
    }

    if (response.status === 422) {
      const error = await response.json();
      console.error('Error de Validación:', error.details);
      throw new Error('Datos de usuario inválidos');
    }

    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After');
      console.error('Límite de tasa excedido. Reintentar después de:', retryAfter);
      throw new Error(`Demasiadas solicitudes. Reintentar después de ${retryAfter} segundos`);
    }

    // Verificar si la respuesta es exitosa (2xx)
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }

    return await response.json();

  } catch (error) {
    console.error('Error creando usuario:', error);
    throw error;
  }
};

// Uso con manejo de errores
const newUser = {
  name: 'Alice Smith',
  email: '[email protected]'
};

createUser(newUser)
  .then(user => console.log('Creado:', user))
  .catch(error => {
    // Mostrar mensaje de error amigable
    alert(`Fallo al crear usuario: ${error.message}`);
  });

Python (librería requests):

import requests
import time

def create_user(user_data):
    try:
        response = requests.post(
            'https://api.example.com/users',
            json=user_data,
            headers={'Authorization': 'Bearer YOUR_TOKEN'}
        )

        # Manejar errores 4xx específicos
        if response.status_code == 400:
            error = response.json()
            print(f'Bad Request: {error}')
            raise ValueError(f"Validación fallida: {error.get('details')}")

        if response.status_code == 401:
            print('No autorizado - token expirado o faltante')
            # Redirigir al login o refrescar token
            raise PermissionError('Autenticación requerida')

        if response.status_code == 403:
            error = response.json()
            print(f"Prohibido: {error.get('message')}")
            raise PermissionError('Permisos insuficientes')

        if response.status_code == 404:
            print('Recurso no encontrado')
            raise FileNotFoundError('Usuario no encontrado')

        if response.status_code == 409:
            error = response.json()
            print(f"Conflicto: {error.get('message')}")
            raise ValueError('El usuario ya existe')

        if response.status_code == 422:
            error = response.json()
            print(f"Error de Validación: {error.get('details')}")
            raise ValueError('Datos de usuario inválidos')

        if response.status_code == 429:
            retry_after = response.headers.get('Retry-After')
            print(f'Límite de tasa excedido. Reintentar después de: {retry_after}')

            # Esperar y reintentar
            if retry_after:
                time.sleep(int(retry_after))
                return create_user(user_data)  # Reintentar

            raise Exception('Demasiadas solicitudes')

        # Verificar si la respuesta es exitosa (2xx)
        response.raise_for_status()

        return response.json()

    except requests.exceptions.RequestException as e:
        print(f'Error creando usuario: {e}')
        raise

# Uso con manejo de errores
new_user = {
    'name': 'Alice Smith',
    'email': '[email protected]'
}

try:
    user = create_user(new_user)
    print(f'Creado: {user}')
except Exception as error:
    print(f'Fallo al crear usuario: {error}')

Diagrama

graph TB
    subgraph "Errores 4xx del Cliente"
        E400[400 Bad Request
Sintaxis/datos inválidos] E401[401 Unauthorized
Autenticación faltante] E403[403 Forbidden
Sin permiso] E404[404 Not Found
Recurso faltante] E409[409 Conflict
Duplicado/conflicto de estado] E422[422 Unprocessable
Validación fallida] E429[429 Too Many
Límite de tasa] end subgraph "Acciones del Cliente" A1[Corregir sintaxis de solicitud] A2[Agregar/refrescar token] A3[Solicitar acceso] A4[Verificar URI] A5[Modificar datos] A6[Corregir validación] A7[Esperar y reintentar] end E400 --> A1 E401 --> A2 E403 --> A3 E404 --> A4 E409 --> A5 E422 --> A6 E429 --> A7

Analogía

Piensa en los errores 4xx como problemas en la entrada de un restaurante:

  • 400 Bad Request → “Tu formulario de pedido está incompleto”
  • 401 Unauthorized → “Necesitas mostrar ID para entrar”
  • 403 Forbidden → “Esta es la sección VIP, no puedes entrar”
  • 404 Not Found → “Ese plato no está en nuestro menú”
  • 409 Conflict → “Ya tienes una reserva”
  • 429 Too Many Requests → “Estás ordenando demasiado rápido, ve más despacio”

Buenas Prácticas

  1. Usar Códigos 4xx Específicos - No usar 400 para todo; usar 401/403/404/422 apropiadamente
  2. Incluir Detalles del Error - Proporcionar mensajes útiles explicando qué salió mal
  3. Validar Temprano - Devolver 400/422 antes de procesar para evitar recursos desperdiciados
  4. Devolver Errores a Nivel de Campo - Incluir errores de validación específicos para cada campo
  5. Usar WWW-Authenticate - Incluir encabezado con 401 para indicar método de autenticación
  6. Establecer Retry-After - Incluir encabezado con 429 para indicar cuándo reintentar
  7. Registrar Errores 4xx - Rastrear errores del cliente para identificar problemas de usabilidad de la API

Errores Comunes

  • 400 para Todo - Usar 400 Bad Request para todos los errores en lugar de códigos específicos
  • Confusión 401 vs 403 - Mezclar autenticación (401) vs autorización (403)
  • Sin Detalles del Error - Devolver errores genéricos sin mensajes útiles
  • Exponer Info Interna - Incluir stack traces o errores de base de datos en respuestas 4xx
  • Sin Retry-After - No indicar a los clientes cuándo reintentar después de limitación de tasa
  • Formato Inconsistente - Cambiar estructura de respuesta de error entre endpoints
  • 404 para Fallos de Auth - Devolver 404 para ocultar existencia de recursos (seguridad por oscuridad)

Estándares y RFCs

Términos Relacionados