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 import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.shannon-ai.com/v1",
});
try {
await client.chat.completions.create({
model: "no-such-model",
messages: [{ role: "user", content: "Hello" }],
});
} catch (error) {
if (!(error instanceof OpenAI.APIError)) throw error;
console.log(error.status); // 400
console.log(error.type); // invalid_request_error
console.log(error.error.message); // unknown model: no-such-model
} curl -i https://api.shannon-ai.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "no-such-model",
"messages": [{"role": "user", "content": "Hello"}]
}' Devuelve el estado 400 y este cuerpo:
{
"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.
{
"error": {
"type": "authentication_error",
"message": "Invalid API key",
"code": "invalid_api_key",
"param": null
}
} {
"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) import OpenAI from "openai";
// maxRetries: 0 — this loop does the retrying, the SDK does not add its own.
const client = new OpenAI({ apiKey: "YOUR_API_KEY", baseURL: "https://api.shannon-ai.com/v1", maxRetries: 0 });
const sleep = (seconds) => new Promise((resolve) => setTimeout(resolve, seconds * 1000));
async function createWithRetry(request, attempts = 5) {
for (let attempt = 0; attempt < attempts; attempt++) {
try {
return await client.chat.completions.create(request);
} catch (error) {
const text = String(error.message);
const flood = text.match(/Retry in (\d+)s/);
if (error.status === 429 && flood) {
await sleep(Number(flood[1])); // flood protection: wait out the minute
} else if (error.status === 429 && text.includes("temporarily busy")) {
await sleep(2 ** attempt);
} else if (error.status >= 500 || error instanceof OpenAI.APIConnectionError) {
await sleep(2 ** attempt); // 1, 2, 4, 8 seconds
} else {
throw error; // wrong request, wrong key or balance
}
}
}
throw new Error("the request failed after all attempts");
}
const response = await createWithRetry({
model: "shannon-3",
messages: [{ role: "user", content: "Hello" }],
});
console.log(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.