Errores sobre los que puedes actuar

· Hyperouter · 5 min de lectura

Todo error de Hyperouter usa el objeto de error de OpenAI y un estado HTTP equivalente. El código que ya gestiona los errores de OpenAI gestiona la mayoría de estos. Esta guía cubre lo que merece una acción y lo que no, y termina con un bucle de reintentos.

El objeto de error

{
  "error": {
    "message": "n must be an integer from 1 to 128",
    "type": "invalid_request_error",
    "code": "invalid_body",
    "param": "n"
  }
}
CampoSignificado
messageUna frase que dice qué está mal. No la analices.
typeLa clase de error.
codeUn identificador estable de la causa. Compara por este campo.
paramEl campo de la solicitud que falla, o null.

Algunos errores añaden generation_id: el id de un trabajo que sigue en curso o que ya se ejecutó. Consúltalo con GET /v1/generations/{id}.

Estados que conviene gestionar

EstadoQué significaQué hacer
400La solicitud no es válida. code indica la causa y param el campo.Corrige la solicitud. No la reintentes sin cambios.
401La clave de API falta o no es válida.Revisa la clave.
402insufficient_credits: tu saldo, o el límite de la clave, no puede cubrir lo máximo que podría costar la solicitud.Recarga, o sube el límite de la clave.
403La cuenta está suspendida, o una función o un modelo no está habilitado para ella.Abre un ticket de soporte.
404No existe ese id, es el id de otra cuenta, o es un vídeo pasado su expires_at.No reintentes.
409No está en un estado que permita la llamada, o hay un conflicto de Idempotency-Key.Consulta reintentar con seguridad.
429Todos los proveedores están a su capacidad, o tu cuenta supera sus límites de velocidad.Espera Retry-After y reintenta.
500, 501Hyperouter falló, o el endpoint no está implementado. No se factura.Reintenta un 500 con cuidado, como se explica abajo.
503Un proveedor falló o no respondió a tiempo.Reintenta, como se explica abajo.

Tres códigos que encontrarás

La misma familia incluye venue_busy (429): todos los proveedores de un modelo de chat superan su presupuesto de velocidad.

Por qué 503, y no 502 ni 504

Hyperouter nunca responde 502 ni 504. Te llegarían como una página simple de nuestra red perimetral, sin el cuerpo del error, así que no podrías leer code.

Donde una pasarela respondería con ellos, recibes 503 con el objeto de error habitual, y el estado de la pasarela en el encabezado de respuesta X-Upstream-Status: 502 cuando un proveedor falló, 504 cuando agotó el tiempo. El code indica qué tipo de fallo fue: all_venues_failed, upstream_error o timeout.

HTTP/2 503
x-upstream-status: 504

Lee X-Upstream-Status si quieres distinguir un fallo de un tiempo agotado. Para la mayoría del código, 503 y code bastan.

Cuándo no reintentar

Reintentar no siempre es seguro. Un reintento puede iniciar un segundo trabajo o un segundo cargo. Hyperouter marca esas respuestas con x-should-retry: false:

En chat, un proveedor que ha aceptado una solicitud puede estar ejecutándola. Si después deja de responder, la solicitud no se envía a otro proveedor: la respuesta es 503 con el código timeout o upstream_error, y se factura sobre su retención. Consúltala con GET /v1/generations/{id} antes de decidir enviarla de nuevo.

Un bucle de reintentos con espera creciente

Reintenta 429 y 503, respeta Retry-After, abandona ante x-should-retry: false y envía una Idempotency-Key en todo lo que cueste dinero.

import random
import time
import uuid

import requests

def post_with_retry(url, headers, body, attempts=5):
    headers = {**headers, "Idempotency-Key": str(uuid.uuid4())}  # same key on every attempt
    for attempt in range(attempts):
        r = requests.post(url, headers=headers, json=body, timeout=120)
        if r.status_code < 400:
            return r.json()
        if r.headers.get("x-should-retry") == "false":
            r.raise_for_status()
        if r.status_code not in (429, 503) or attempt == attempts - 1:
            r.raise_for_status()
        wait = float(r.headers.get("Retry-After", 2 ** attempt))
        time.sleep(wait + random.random())  # jitter

En una solicitud de chat, el reintento recibe 409 idempotency_key_replayed si el primer intento sí se ejecutó. Esa es la señal para leer el registro con GET /v1/generations/{id} y parar.

Los SDK de OpenAI ya reintentan dos veces un 408, 409, 429 o 5xx y respetan x-should-retry: false, así que quizá no necesites ningún bucle.

Qué sigue