La API de Doubao se ofrece a través de Volcano Ark. Su interfaz se parece a la de OpenAI, pero la semántica de los errores tiene particularidades. La más típica es el 404: el servicio no está caído; el problema suele estar en el ID del endpoint de inferencia, el nombre del modelo, la región o la ruta. El 429 también se diagnostica mal con frecuencia: puede ser límite de velocidad, concurrencia, cuota, saldo impago o paquete de recursos agotado, y cada caso se trata de forma distinta.

Conviene fijar un orden de diagnóstico de cinco pasos: mirar el código de estado HTTP, leer code y message del cuerpo, obtener el ID de solicitud, reproducir con una solicitud mínima y solo entonces cambiar parámetros o modelo. Este artículo recorre 401, 404, 400, 429, timeouts y 5xx, con código de manejo de errores y una lista de registro. Los modelos y la facturación se rigen por los precios de modelos.

Nota: Doubao, Volcano Ark y Volcano Engine son marcas de ByteDance. Este artículo solo ofrece una explicación técnica y no implica subordinación, representación ni asociación con ByteDance. Los códigos de error, los criterios de limitación y el comportamiento de la interfaz pueden cambiar según la versión; prevalecen la documentación oficial y la respuesta real.

1. Separa primero las dos capas de error

El código de estado HTTP indica el resultado en la capa de protocolo; code, type y message del cuerpo explican la causa concreta. Un mismo 401 puede significar que falta la clave, que está mal escrita, que venció o que el proyecto no tiene permiso. Un mismo 429 puede ser solicitudes demasiado rápidas, concurrencia alta, cuota agotada o saldo impago. Si solo recuerdas el código de estado, en el diagnóstico solo puedes adivinar.

HTTP Significado habitual Escenario en Doubao ¿Reintentar?
401 Fallo de autenticación Clave ausente, formato incorrecto, vencida o de otra región No; corrige primero las credenciales
403 Sin permiso La clave no tiene permiso sobre el modelo o el endpoint No; revisa la autorización
404 Recurso inexistente ID de endpoint, nombre de modelo, ruta o región incorrectos No; verifica primero
400 Parámetro incorrecto Contexto excedido, max_tokens inválido, formato de imagen no soportado No; corrige la solicitud
429 Límite o cuota RPM, TPM o concurrencia excedidos; saldo impago o paquete agotado Depende de la causa
500/502/503 Error del servidor Falla del origen, inestabilidad del gateway, indisponibilidad breve Sí; retroceso y reintento
Timeout Timeout de conexión o lectura Red, cola, entrada demasiado larga, salida demasiado larga Reintento limitado

Los nombres de campo dependen de la respuesta real: a veces están en error.code y a veces en el nivel superior; el código no debe asumir una sola estructura.

2. 401: las cuatro causas de un fallo de autenticación

El 401 no tiene relación con la capacidad del modelo ni con el saldo; revisa primero la cadena de credenciales:

  • ¿El encabezado es Authorization: Bearer <key>, con un espacio después de los dos puntos?
  • ¿La clave se carga correctamente desde la variable de entorno, sin comillas, espacios ni saltos de línea alrededor?
  • ¿La clave fue eliminada, rotada o está fuera de vigencia?
  • ¿Coinciden el proyecto, la región y la Base URL a los que pertenece la clave?

Las claves copiadas desde una ventana de chat suelen arrastrar caracteres invisibles. Envía primero una solicitud mínima que devuelva una sola palabra; así descartas la mayoría de los errores básicos. Si la misma clave funciona en la consola pero el código devuelve 401, revisa la variable de entorno y si el proxy inverso está descartando el encabezado de autenticación.

3. 404: el ID de endpoint y el nombre de modelo se confunden con facilidad

En Volcano Ark hay dos formas habituales de especificar el modelo: el endpoint de inferencia creado en la consola, que normalmente empieza con ep-, y el ID de modelo directo, que admiten algunos modelos. Ambos se colocan en el campo model, pero los permisos, los límites y el alcance pueden ser distintos. Mezclar el ID de endpoint con el nombre del modelo, o copiar el endpoint de otro proyecto, produce un 404 o un "recurso inexistente" similar.

Orden de diagnóstico:

  1. Confirma que la Base URL sea una dirección regional como https://ark.cn-beijing.volces.com/api/v3, sin agregar ni omitir /api/v3.
  2. Verifica en la consola que el endpoint exista, pertenezca al proyecto actual y no esté desactivado.
  3. Confirma si el método de llamada actual exige el ID de endpoint o el ID de modelo; no lo completes de memoria.
  4. Envía una solicitud mínima con curl para descartar el efecto del SDK al concatenar rutas y de las reescrituras del proxy.
  5. Mira el tipo de contenido de la respuesta: si devuelve una página de error HTML, es posible que la solicitud no haya llegado a Ark y que la haya interceptado un gateway o un proxy inverso.

Un 404 con HTML suele ser un problema de ruta o de proxy; un 404 con cuerpo JSON de error se parece más a un problema de modelo o de endpoint.

4. 400: contexto excedido y parámetros incorrectos

El 400 abarca muchos casos; el más común es el contexto excedido. La ventana de contexto suele calcularse como tokens de entrada más tokens de salida, y un max_tokens demasiado grande puede hacer que una solicitud que antes pasaba se salga del límite. Las imágenes, las definiciones de herramientas y los mensajes históricos también consumen tokens.

La solución no es subir el límite, sino dejar margen a la entrada:

  • Comprime o divide el prompt de sistema y elimina reglas duplicadas.
  • Resume el historial de conversación y conserva solo los últimos turnos y los hechos clave.
  • En documentos largos, recupera primero y luego concatena; no pegues el texto completo.
  • Comprime las imágenes y confirma que el formato y el tamaño estén dentro del rango soportado.
  • Revisa que temperature y top_p no estén fuera de rango y que los roles de mensaje sean válidos.

El conteo de tokens es una estimación; cerca del límite conviene dejar un margen del 10 % al 20 %.

5. 429: límite de velocidad, concurrencia y cuota se tratan por separado

El 429 tiene cuatro causas:

  • Límite de velocidad: la cantidad de solicitudes o de tokens por minuto supera el plan; tras un retroceso suele recuperarse.
  • Límite de concurrencia: hay demasiadas solicitudes en vuelo; hay que reducir la concurrencia o hacer cola, porque reintentar sin más agrava la congestión.
  • Cuota o paquete de recursos agotado: el saldo se terminó; reintentar no sirve, hay que recargar o ajustar el plan.
  • Estado anómalo de la cuenta: saldo impago, verificación pendiente o cambio de permisos; sigue las indicaciones de la consola.

Si la respuesta trae Retry-After, espera ese tiempo; si no, aplica retroceso exponencial con jitter aleatorio. Los 429 por cuota o por saldo impago no deben reintentarse, porque solo alargan la interrupción.

En streaming, un 429 puede ocurrir al establecer la conexión o aparecer como un evento de error dentro del flujo. El cliente debe manejar tanto el código de estado como los errores dentro del flujo; no basta con mirar el primer fragmento.

6. Timeouts y cortes de streaming: localiza en tres tramos

El timeout debe dividirse en tres tramos:

  • Timeout de conexión: DNS, TLS, proxy de salida o red inalcanzable; suele fallar en pocos segundos.
  • Timeout del primer token: la solicitud ya llegó al origen, pero está en cola o procesando una entrada larga; se manifiesta como un periodo prolongado sin el primer fragmento.
  • Timeout de lectura: pasa demasiado tiempo entre dos fragmentos, quizá por una salida muy larga, un razonamiento prolongado o una conexión cortada por un equipo intermedio.

Cada tramo necesita su propia configuración. Un timeout total demasiado grande mantiene conexiones ocupadas por solicitudes fallidas; uno demasiado pequeño mata salidas largas válidas. Se sugiere un timeout de conexión de unos 5 segundos, un timeout de lectura basado en el P95 del negocio y un presupuesto total en la capa de aplicación.

En streaming también hay que revisar el buffering del proxy inverso. El búfer retrasa la entrega de los fragmentos y parece que la respuesta "se quedó colgada". Si el flujo se corta, no guardes el contenido incompleto como si fuera un resultado completo.

7. ID de solicitud y registros: la evidencia del diagnóstico

Las respuestas de Volcano Ark suelen incluir un identificador de solicitud; los campos habituales son X-Request-Id y, en algunas interfaces, un ID de registro como X-Tt-Logid. El nombre exacto depende de la respuesta real; conserva los encabezados completos en lugar de elegir un solo campo.

El cliente también debe generar su propio trace_id y mantenerlo igual entre todos los reintentos de una misma solicitud de negocio. El registro debe incluir al menos hora, trace_id, Base URL, modelo o endpoint, código de estado, code y message del error, duración, número de reintentos, si fue streaming y consumo de tokens.

De la clave registra solo los primeros caracteres o un hash; no guardes en disco el texto original del usuario ni la respuesta completa. Del cuerpo de error basta con conservar los primeros 500 caracteres.

8. Reintentos y retroceso: código reutilizable

El siguiente código desactiva los reintentos automáticos del SDK, decide si reintentar según el estado y el contenido del error, respeta Retry-After y genera un trace_id para cada solicitud lógica:

import os
import random
import time
import uuid

import httpx
from openai import OpenAI, APIConnectionError, APIStatusError, APITimeoutError

client = OpenAI(
    api_key=os.environ["ARK_API_KEY"],
    base_url=os.environ.get(
        "ARK_BASE_URL", "https://ark.cn-beijing.volces.com/api/v3"
    ),
    timeout=httpx.Timeout(connect=5.0, read=180.0, write=30.0, pool=10.0),
    max_retries=0,  # Desactiva los reintentos del SDK; los controla la lógica siguiente
)

def parse_error(exc: APIStatusError) -> tuple[str, str]:
    try:
        body = exc.response.json()
    except Exception:
        return "", exc.response.text[:200]
    err = body.get("error", body)
    return str(err.get("code") or err.get("type") or ""), str(err.get("message") or "")

def is_retryable(exc: Exception) -> bool:
    if isinstance(exc, (APITimeoutError, APIConnectionError)):
        return True
    if not isinstance(exc, APIStatusError):
        return False
    if exc.status_code >= 500:
        return True
    if exc.status_code != 429:
        return False
    code, message = parse_error(exc)
    text = f"{code} {message}".lower()
    # Un 429 por cuota, saldo impago o permisos no se reintenta; ajusta las palabras clave al texto real
    return not any(k in text for k in ("quota", "arrears", "balance", "permission"))

def ask(prompt: str, max_attempts: int = 4) -> str:
    trace_id = uuid.uuid4().hex[:12]
    for attempt in range(1, max_attempts + 1):
        try:
            resp = client.chat.completions.create(
                model=os.environ["ARK_MODEL"],  # ID de endpoint o ID de modelo
                messages=[{"role": "user", "content": prompt}],
                max_tokens=1024,
            )
            return resp.choices[0].message.content
        except Exception as exc:
            status = getattr(exc, "status_code", "")
            code, message = parse_error(exc) if isinstance(exc, APIStatusError) else ("", "")
            print(
                f"trace={trace_id} attempt={attempt} status={status} "
                f"code={code} message={message[:200]}"
            )
            if attempt == max_attempts or not is_retryable(exc):
                raise
            retry_after = getattr(
                getattr(exc, "response", None), "headers", {}
            ).get("retry-after")
            try:
                wait = float(retry_after) if retry_after else min(2 ** (attempt - 1), 20)
            except ValueError:
                wait = min(2 ** (attempt - 1), 20)
            time.sleep(wait + random.uniform(0, 1))
    raise RuntimeError("Se agotaron los reintentos")

Tres reglas adicionales: para solicitudes con efectos secundarios, confirma primero el estado con una clave de idempotencia; controla el presupuesto de reintentos de forma global; y monitorea la proporción de 429 y la latencia P95, reduciendo la concurrencia cuando haya un aumento brusco.

9. Lista de comprobación antes de contactar al soporte

  1. Hora y zona horaria del incidente, y si es continuo o intermitente.
  2. Base URL, ruta completa, nombre de modelo o ID de endpoint.
  3. Código de estado HTTP, code del error, message e ID de solicitud.
  4. Una solicitud curl mínima y reproducible, con la clave enmascarada.
  5. Si usa streaming, contexto largo, imágenes o llamadas a herramientas.
  6. Si se cambiaron recientemente la clave, el endpoint, la cuota o el proxy.
  7. Alcance: una sola clave, un proyecto, una región o todo el servicio.

Con esta información, el equipo de soporte puede contrastar directamente los registros del origen. Después de cada incidente, agrega una métrica o una alerta.

10. Preguntas frecuentes (FAQ)

1. ¿Un 404 significa siempre que el modelo fue retirado?

No necesariamente. Lo más común es un ID de endpoint o un nombre de modelo incorrecto, un endpoint eliminado, una región que no coincide o una ruta a la que le falta /api/v3. Mira primero si la respuesta es JSON o una página HTML de error y luego verifica punto por punto.

2. ¿Cuántas veces conviene reintentar un 429?

Para límites de velocidad y congestión breve, reintenta 2 o 3 veces con retroceso exponencial. No reintentes los 429 por cuota agotada, saldo impago o permisos insuficientes; atiende primero la cuenta o el plan. Respeta Retry-After.

3. ¿Cómo se calcula la longitud del contexto?

Normalmente como tokens de entrada más tokens de salida, y max_tokens también consume del total. Las imágenes, las definiciones de herramientas y los mensajes históricos cuentan como entrada; deja margen cuando te acerques al límite.

4. ¿Cómo se configura el timeout de una solicitud en streaming?

El timeout de lectura suele limitar el intervalo entre dos fragmentos, no la duración total. El timeout de conexión se configura aparte, y la capa de aplicación añade un presupuesto total y un timeout de inactividad. El búfer del proxy retrasa la entrega de los fragmentos.

5. ¿Qué hago si aumentan los errores después de integrar un gateway?

Primero confirma si el error lo devuelve el gateway o el origen: revisa el formato del cuerpo, el código de estado y el ID de solicitud. Conserva el trace ID y compara una solicitud mínima por conexión directa y por gateway. La mayoría de los problemas están en la concatenación de rutas, el reenvío del encabezado de autenticación o los timeouts.

Próximos pasos

Crea primero una clave de prueba en ZSQH AI Gateway, practica una vez cada caso —401, 404, 429 y timeout— y después conecta el monitoreo de producción. Los modelos disponibles y la facturación se consultan en los precios de modelos.

Lecturas relacionadas