Saltar al contenido
Manejo de errores

Manejo de errores

Una solicitud que falla devuelve un código de estado HTTP y un cuerpo JSON con un tipo de error y un mensaje. Esta página lista todos los estados, muestra el cuerpo de error de cada formato de API e indica qué errores vale la pena reenviar.

Los SDK lanzan un objeto de error que lleva el código de estado y los campos del cuerpo. Esta solicitud nombra un modelo que no existe:

import openai
from openai import OpenAI

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://api.shannon-ai.com/v1",
)

try:
    client.chat.completions.create(
        model="no-such-model",
        messages=[{"role": "user", "content": "Hello"}],
    )
except openai.APIStatusError as error:
    print(error.status_code)                           # 400
    print(error.type)                                  # invalid_request_error
    print(error.body["message"])                       # unknown model: no-such-model
    print(error.response.headers.get("x-request-id"))  # the id of this request

Devuelve el estado 400 y este cuerpo:

400 JSON
{
  "error": {
    "type": "invalid_request_error",
    "message": "unknown model: no-such-model"
  }
}

Cuerpo del error

Hay dos cuerpos. /v1/messages responde en el formato de Anthropic; todos los demás endpoints responden en el formato de OpenAI. En ambos, los detalles están en error.type y error.message, de modo que un solo lector sirve para todos los endpoints.

401 Formato OpenAI
{
  "error": {
    "type": "authentication_error",
    "message": "Invalid API key",
    "code": "invalid_api_key",
    "param": null
  }
}
401 Formato Anthropic
{
  "type": "error",
  "error": {
    "type": "authentication_error",
    "message": "Invalid API key"
  }
}
Campo Tipo Descripción
error.type string El tipo de error. Consulta Tipos de error más abajo.
error.message string Una frase para una persona. Los textos de las tablas siguientes son estables salvo en sus partes variables, mostradas como <id>, <n> o ….
error.code string | null Presente en algunos errores del formato OpenAI: invalid_api_key con 401, request_too_large con 413, context_length_exceeded cuando una conversación es demasiado larga; en los demás casos null o ausente. Decide primero según el estado y error.type y trata code como un extra.
error.param null Presente en los mismos errores que code. Siempre null.
type string Solo en el formato Anthropic. Siempre error.

Tipos de error

Tipo Estados Descripción
invalid_request_error 400, 413, 415, 422 La solicitud en sí es incorrecta: un campo, el cuerpo, el id del modelo o una entrada que el modelo no acepta. Cambia la solicitud antes de enviarla de nuevo.
authentication_error 401 La clave API falta, es desconocida o está revocada.
not_found_error 404 La ruta no existe.
rate_limit_error 429 El saldo no cubre la solicitud, la protección contra flood la detuvo o el modelo está ocupado. El mensaje indica cuál.
api_error 500, 502, 503, 504 La solicitud era correcta y el fallo está de nuestro lado. Envíala de nuevo.
server_error 500 La solicitud era correcta y el fallo está de nuestro lado. Envíala de nuevo.

Códigos de estado

Estado Tipo Mensaje Cuándo
400 invalid_request_error Failed to parse the request body as JSON: … El cuerpo no es JSON válido.
400 invalid_request_error unknown model: <id> model no es un id publicado. En /v1/responses también cuando falta model.
400 invalid_request_error No user message provided Niveles de Shannon: la solicitud no contiene texto de usuario.
400 invalid_request_error <id> does not accept image input Se envió una imagen a un modelo de pesos abiertos alojado sin entrada de imágenes.
400 invalid_request_error audio and video input are not supported by any hosted model Se envió una parte de audio o video a un modelo de pesos abiertos alojado.
400 invalid_request_error <id> does not accept response_format Se pidió salida estructurada a un modelo de pesos abiertos alojado que no la tiene.
400 invalid_request_error <id> does not accept response_format json_schema; use json_object Se envió un esquema JSON a un modelo de pesos abiertos alojado que solo acepta json_object.
400 invalid_request_error unknown reasoning effort '<value>'; expected off, low, medium or high El esfuerzo de razonamiento no es uno de los valores listados. Modelos de pesos abiertos alojados.
400 invalid_request_error count_tokens is available for the hosted open models; unknown model: <id> Se pidió el conteo de tokens para un modelo que no es de los modelos de pesos abiertos alojados.
401 authentication_error Missing authentication No se envió ninguna clave API.
401 authentication_error Invalid API key La clave es desconocida, fue reemplazada por una más nueva o fue revocada.
404 not_found_error Not Found La ruta no existe, por ejemplo /v1/embeddings.
405 api_error Method Not Allowed La ruta existe, pero no con este método HTTP.
413 invalid_request_error Failed to buffer the request body: … El cuerpo de la solicitud es mayor que 32 MiB.
415 invalid_request_error Expected request with `Content-Type: application/json` El header Content-Type falta o no es application/json.
422 invalid_request_error Failed to deserialize the JSON body into the target type: … Falta un campo obligatorio, como messages, o un campo tiene un tipo JSON incorrecto.
429 rate_limit_error Quota exceeded. Upgrade your plan at shannon-ai.com/plan El límite de salida de la solicitud es mayor que lo que queda de tu saldo.
429 rate_limit_error Too many requests. Retry in <n>s. Protección contra flood: más de 120 solicitudes en un minuto en tu cuenta. <n> es el número de segundos hasta que termine el minuto.
429 rate_limit_error Shannon Coder call quota reached. Upgrade your plan at shannon-ai.com/plan shannon-coder-1 en /v1/chat/completions y /v1/messages: se agotaron las llamadas de Shannon Coder de tu plan para la ventana actual de 4 horas.
429 rate_limit_error Shannon routes are temporarily busy. Please retry. El modelo está ocupado en este momento.
500 server_error The model backend failed to answer. Please retry. El modelo no produjo una respuesta.
500 server_error An internal error occurred. Please retry. Un fallo interno mientras se preparaba la solicitud.
502 api_error The model backend failed to answer. Please retry. El modelo no produjo una respuesta. Familia Shannon 3 y modelos de pesos abiertos alojados.
503 api_error Could not verify your quota right now. Please retry. No se pudo leer tu saldo en este momento.
503 api_error token counting is temporarily unavailable for this model El conteo de tokens no está disponible para este modelo en este momento.
504 api_error upstream timed out Un modelo de pesos abiertos alojado tardó más que el límite de tiempo de una solicitud sin streaming.

Las comprobaciones se ejecutan en este orden: clave API, cuerpo de la solicitud, id del modelo, protección contra flood, saldo. Por eso una solicitud con una clave incorrecta y un cuerpo incorrecto devuelve 401. Una solicitud rechazada con un estado 4xx no se cobra.

Saldo y protección contra flood

Todas las solicitudes se atienden por igual: no hay niveles de límite de tasa ni una cuota de API separada. Aun así, dos cosas pueden responder 429, ambas con el tipo rate_limit_error. El mensaje las distingue.

Saldo

Tu saldo es la cuota del plan de hoy, que se restablece a las 00:00 UTC, y después tu crédito comprado. El chat y la API toman del mismo saldo.

Cada solicitud reserva primero su límite de salida: max_tokens, o max_output_tokens en /v1/responses, 4,096 cuando no envías ninguno. Cuando la respuesta está completa, la cantidad reservada se sustituye por lo que usó la solicitud. Si la cantidad no cabe en el saldo restante, la solicitud devuelve Quota exceeded antes de que el modelo empiece.

Las solicitudes que se ejecutan al mismo tiempo reservan cada una su propia cantidad. Por eso, muchas solicitudes en paralelo con un max_tokens alto pueden agotar el saldo por un momento, aunque cada respuesta sea corta. Un max_tokens menor reserva menos.

Protección contra flood

La protección contra flood permite 120 solicitudes por minuto por cuenta. Cuenta la cuenta, no la clave, sumando /v1/chat/completions, /v1/responses y /v1/messages. El minuto empieza con tu primera solicitud.

La solicitud número 121 dentro de ese minuto devuelve Too many requests. Retry in <n>s. y no cuesta nada. Las solicitudes rechazadas también cuentan, así que un cliente que reenvía sin esperar sigue siendo rechazado hasta que termine el minuto. Espera los <n> segundos del mensaje.

Las solicitudes enviadas en paralelo no se rechazan por ser paralelas. Esperan en cola y se responden por turno.

GET /v1/models y los endpoints de conteo de tokens no se cuentan.

Más sobre límites y saldo, y dónde ver tu propio saldo y uso: Límites y saldo Claves y uso

Qué reintentar

Reenvía una solicitud solo cuando la misma solicitud pueda tener éxito. Los SDK de OpenAI y Anthropic ya reintentan por sí solos dos veces las respuestas 429 y 5xx; define max_retries (maxRetries en JavaScript) para cambiarlo.

Error ¿Reenviar? Cómo
400, 404, 405, 413, 415, 422 No. Corrige primero la solicitud. El mensaje indica el campo, el modelo o la entrada que es incorrecto.
401 No. Usa una clave válida. Crea o sustituye la clave en la página Claves y uso.
429 Too many requests Sí, tras la espera. Espera el número de segundos del mensaje y luego envía la misma solicitud.
429 Quota exceeded Tal como está, no. Baja max_tokens, espera al restablecimiento diario a las 00:00 UTC o añade crédito.
429 Shannon Coder call quota reached Sí, en la siguiente ventana. Las ventanas empiezan cada 4 horas desde las 00:00 UTC. Otro modelo responde de inmediato.
429 … temporarily busy Sí. Espera unos segundos y luego más tiempo en cada intento posterior.
500, 502, 503 Sí. Espera 1 segundo, luego 2, 4 y 8. Detente tras unos pocos intentos e informa del id de la solicitud.
504 Sí. Envíala con stream en true: el límite de tiempo se aplica a las respuestas sin streaming.

Un bucle de reintentos que sigue esta tabla:

import re
import time

import openai
from openai import OpenAI

# max_retries=0: this loop does the retrying, the SDK does not add its own.
client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.shannon-ai.com/v1", max_retries=0)


def create_with_retry(attempts=5, **request):
    for attempt in range(attempts):
        try:
            return client.chat.completions.create(**request)
        except openai.RateLimitError as error:
            text = str(error)
            flood = re.search(r"Retry in (\d+)s", text)
            if flood:
                time.sleep(int(flood.group(1)))   # flood protection: wait out the minute
            elif "temporarily busy" in text:
                time.sleep(2 ** attempt)
            else:
                raise                             # balance: a wait of seconds does not help
        except (openai.InternalServerError, openai.APIConnectionError):
            time.sleep(2 ** attempt)              # 1, 2, 4, 8 seconds
    raise RuntimeError("the request failed after all attempts")


response = create_with_retry(
    model="shannon-3",
    messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)

Errores dentro de un stream

Un stream tiene el estado 200 desde su primer byte. Un fallo posterior llega como un evento dentro del stream: un fragmento con un miembro error en /v1/chat/completions, un evento error en /v1/messages, un evento response.failed o error en /v1/responses. Los eventos de cierre del stream siguen llegando. Trata esa respuesta como fallida y envía la solicitud de nuevo, como con un estado 5xx.

Una conversación más larga que la ventana de contexto del modelo se informa de esta manera, con el tipo invalid_request_error y el código context_length_exceeded. Acorta la conversación antes de volver a enviarla.

Las tramas exactas se describen en la sección de streaming. Streaming

Ids de solicitud

Cada respuesta, errores incluidos, tiene un header x-request-id. Es el valor que enviaste en tu propio header x-request-id, o 12 caracteres hexadecimales si no enviaste ninguno. Regístralo y cítalo cuando pidas ayuda con una solicitud.