500, 502, 503 y 504 errores de las API: qué significan y cómo controlarlos

Un código de estado de 500 a 599 significa que el servidor no pudo completar una solicitud que parecía válida. La solicitud no es el problema, por lo que enviarla de nuevo puede funcionar. Que debas volver a enviarla depende del código de estado y de lo que haga la solicitud. Para ver las definiciones, vea RFC 9110, sección 15.6.

Qué significa cada código de estado

Código de estado Qué significa ¿Reintentar?
500 Internal Server Error El servidor alcanzó una condición que no esperaba Depende de la API. Algunas API, como la API de Claude, indican que hay que reintentar un error 500 con retroceso exponencial. Consulte los documentos de la API.
502 Bad Gateway Una puerta de enlace o servidor proxy recibió una respuesta no válida del servidor que hay detrás de ella Sí, si la solicitud es segura para repetirse
503 Service Unavailable El servidor está temporalmente sobrecargado o inactivo por mantenimiento, y debe recuperarse después de algún tiempo. El servidor puede enviar un encabezado Retry-After. Sí, tras el Retry-After tiempo, si el servidor lo ha enviado
504 Gateway Timeout Una puerta de enlace o proxy no recibió respuesta a tiempo del servidor que hay detrás Sí, si la solicitud es segura para repetirse. La pasarela dejó de esperar, por lo que no sabes si el servidor ha realizado la tarea.

Qué solicitudes son seguras para reintentar

La RFC 9110 define un método como idempotente cuando enviar la misma solicitud varias veces tiene el mismo efecto que enviarla una sola vez. GET, HEAD, OPTIONS, TRACE, PUTy DELETE son idempotentes. POST y PATCH no lo son. Según la RFC, un cliente no debería reintentar automáticamente una solicitud con un método no idempotente, a menos que sepa que la solicitud es idempotente de todos modos, o que pueda determinar que el servidor nunca procesó la solicitud original. Para obtener más información, consulte Métodos Idempotentes.

Un reintento POST tras un error 502 o 504 puede generar un segundo pedido o enviar un segundo correo electrónico. Algunas bibliotecas de reintento vuelven a intentar cada método de forma predeterminada. Por ejemplo, el controlador de resistencia estándar de .NET vuelve a intentar POST a menos que llame a options.Retry.DisableForUnsafeHttpMethods(). Para más detalles, consulta Crear aplicaciones HTTP resilientes.

Retry-After en un 503

Un 503 puede incluir un encabezado Retry-After. Su valor es bien un número de segundos, como 120, bien una fecha HTTP, como Fri, 31 Dec 1999 23:59:59 GMT. Tu código debe administrar ambos casos. Para obtener más información, consulte Retry-After.

Dejar de llamar a una API que sigue fallando

Los reintentos ayudan con fallos breves. Cuando una API está inactiva durante minutos, reintentar cada solicitud agrega carga a un servidor que ya tiene problemas y los usuarios esperan a que se produzca un error en cada reintento. Un disyuntor de circuito realiza un seguimiento de los fallos y, cuando hay demasiados, deja de llamar a la API durante un tiempo y devuelve un error rápido. Después de ese tiempo, permite que se realicen algunas solicitudes para comprobar si se recuperó la API. Para obtener más información, consulte Patrón circuit breaker. El gestor de resiliencia estándar de .NET incluye un cortacircuitos que se activa durante 5 segundos cuando al menos el 10 % de las solicitudes fallan en un intervalo de 30 segundos con un mínimo de 100 solicitudes.

Cómo administrar los errores 5xx

  1. Reintenta los errores 502, 503 y 504 solo en el caso de solicitudes idempotentes. Para POST y PATCH, vuelve a intentarlo solo si la API documenta una forma de repetirlas de manera segura.
  2. Espere antes de reintentar. Utiliza Retry-After cuando el servidor lo envíe. De lo contrario, utiliza un retroceso exponencial con fluctuación aleatoria y detente tras unos pocos intentos.
  3. Consulta la documentación de la API para el error 500. Reinténtelo solo si la API dice que es seguro.
  4. Deje de llamar a una API que sigue fallando. Utiliza un disyuntor para que tu aplicación falle rápidamente mientras la API se recupera.
  5. Indique al usuario qué ha ocurrido. Muestra "el servicio tiene problemas, inténtelo de nuevo más tarde" en lugar de un error genérico o una traza de pila.
const RETRYABLE_STATUS = new Set([502, 503, 504]);
const IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"]);

function retryAfterMs(response) {
  const value = response.headers.get("retry-after");
  if (!value) return null;
  const seconds = Number(value);
  if (Number.isFinite(seconds)) return seconds * 1000;
  const date = Date.parse(value);
  return Number.isNaN(date) ? null : Math.max(0, date - Date.now());
}

export async function fetchWithRetry(url, options = {}, maxRetries = 3) {
  const method = (options.method ?? "GET").toUpperCase();
  const canRetry = IDEMPOTENT_METHODS.has(method);

  for (let attempt = 0; ; attempt++) {
    const response = await fetch(url, options);
    if (!RETRYABLE_STATUS.has(response.status) || !canRetry || attempt === maxRetries) {
      return response;
    }
    await response.body?.cancel();
    const backoff = 2 ** attempt * 1000 + Math.random() * 1000;
    const wait = retryAfterMs(response) ?? backoff;
    await new Promise((resolve) => setTimeout(resolve, wait));
  }
}

Cómo probar que la aplicación controla los errores 5xx

Rara vez ve un 5xx mientras desarrolla y no puede hacer que una API produzca un error a petición. La forma en que realices las pruebas determina si detectas los errores antes que tus usuarios.

Approach Lo que encuentras Lo que te pierdes
A la espera de la producción Interrupciones reales Todo, hasta que un usuario haga clic en ello
Simula la API en tus pruebas o deja que tu agente de codificación escriba el mock Si se ejecuta tu rama de errores Tu cliente HTTP real y la biblioteca de reintentos, y cuántas veces reintenta realmente. La aplicación también necesita un interruptor solo para pruebas para llegar al mock.
Llame a la API real y espere a que se produzca un error. Comportamiento real No se puede hacer que la API falle a petición
Intercepta el tráfico real de tu aplicación y devuelve errores 5xx a la frecuencia que elijas El cliente HTTP real, la biblioteca de reintentos y el circuit breaker Nada en la aplicación cambia, por lo que no prueba el código de forma aislada. Mantenga las pruebas unitarias para eso.

Pruébalo en tu app

El proxy de desarrollo intercepta las solicitudes de la aplicación y hace que una parte de ellas falle con los errores que defina, mediante GenericRandomErrorPlugin. La aplicación sigue llamando a la dirección URL real. Agregue el complemento al archivo de configuración y apunte su errorsFile a un archivo con los errores 5xx. En este ejemplo se usa https://api.contoso.com. Reemplácelo por la dirección URL de la API a la que llama la aplicación.

Archivo: server-errors.json

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.errorsfile.schema.json",
  "errors": [
    {
      "request": {
        "url": "https://api.contoso.com/*"
      },
      "responses": [
        { "statusCode": 500 },
        { "statusCode": 502 },
        {
          "statusCode": 503,
          "headers": [
            { "name": "Retry-After", "value": "10" }
          ]
        },
        { "statusCode": 504 }
      ]
    }
  ]
}

De forma predeterminada, el complemento rechaza el 50 % de las solicitudes. Compruebe en la salida de Dev Proxy que su aplicación reintenta las solicitudes GET y envía cada POST una sola vez. Dev Proxy no comprueba si tu aplicación espera Retry-After ante un error 503, así que compara tú mismo los tiempos de solicitud. A continuación, inicie Dev Proxy con --failure-rate 100 para ver lo que hace la aplicación cuando la API sigue fallando. Para obtener más información, consulta Cambiar la tasa de fallo de las solicitudes. Para instalar Dev Proxy, consulte Configurar Dev Proxy.

Pasos siguientes

Consulte también