Gestió d’errors
Una sol·licitud que falla retorna un codi d'estat HTTP i un cos JSON amb un tipus d'error i un missatge. Aquesta pàgina llista tots els estats, mostra el cos d'error de cada format d'API i indica quins errors val la pena tornar a enviar.
Els SDK llancen un objecte d'error que porta el codi d'estat i els camps del cos. Aquesta sol·licitud indica un model que no existeix:
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"}]
}' Retorna l'estat 400 i aquest cos:
{
"error": {
"type": "invalid_request_error",
"message": "unknown model: no-such-model"
}
} Cos de l'error
Hi ha dos cossos. /v1/messages respon en el format Anthropic; tots els altres endpoints responen en el format OpenAI. En tots dos, els detalls són a error.type i error.message, de manera que un sol lector serveix per a tots els 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"
}
} | Camp | Tipus | Descripció |
|---|---|---|
error.type | string | El tipus d'error. Vegeu Tipus d'error més avall. |
error.message | string | Una frase per a una persona. Els textos de les taules següents són estables llevat de les parts variables, mostrades com a <id>, <n> o …. |
error.code | string | null | Present en alguns errors del format OpenAI: invalid_api_key amb 401, request_too_large amb 413, context_length_exceeded quan una conversa és massa llarga, i en cas contrari null o absent. Decideix primer segons l'estat i error.type i tracta code com a extra. |
error.param | null | Present als mateixos errors que code. Sempre null. |
type | string | Només al format Anthropic. Sempre error. |
Tipus d'error
| Tipus | Estats | Descripció |
|---|---|---|
invalid_request_error | 400, 413, 415, 422 | La sol·licitud en si és incorrecta: un camp, el cos, l'id del model o una entrada que el model no accepta. Canvia la sol·licitud abans de tornar a enviar-la. |
authentication_error | 401 | La clau API falta, és desconeguda o està revocada. |
not_found_error | 404 | El camí no existeix. |
rate_limit_error | 429 | El saldo no cobreix la sol·licitud, la protecció contra inundació l'ha aturada, o el model està ocupat. El missatge indica quina és la causa. |
api_error | 500, 502, 503, 504 | La sol·licitud era correcta i l'error és per part nostra. Torna a enviar-la. |
server_error | 500 | La sol·licitud era correcta i l'error és per part nostra. Torna a enviar-la. |
Codis d'estat
| Estat | Tipus | Missatge | Quan |
|---|---|---|---|
400 | invalid_request_error | Failed to parse the request body as JSON: … | El cos no és JSON vàlid. |
400 | invalid_request_error | unknown model: <id> | model no és un id publicat. A /v1/responses també quan falta model. |
400 | invalid_request_error | No user message provided | Nivells Shannon: la sol·licitud no conté cap text d'usuari. |
400 | invalid_request_error | <id> does not accept image input | S'ha enviat una imatge a un model open-weight hostejat sense entrada d'imatge. |
400 | invalid_request_error | audio and video input are not supported by any hosted model | S'ha enviat una part d'àudio o de vídeo a un model open-weight hostejat. |
400 | invalid_request_error | <id> does not accept response_format | S'ha demanat sortida estructurada a un model open-weight hostejat que no en té. |
400 | invalid_request_error | <id> does not accept response_format json_schema; use json_object | S'ha enviat un esquema JSON a un model open-weight hostejat que només accepta json_object. |
400 | invalid_request_error | unknown reasoning effort '<value>'; expected off, low, medium or high | L'esforç de raonament no és cap dels valors llistats. Models open-weight hostejats. |
400 | invalid_request_error | count_tokens is available for the hosted open models; unknown model: <id> | S'ha demanat el recompte de tokens per a un model fora dels models open-weight hostejats. |
401 | authentication_error | Missing authentication | No s'ha enviat cap clau API. |
401 | authentication_error | Invalid API key | La clau és desconeguda, s'ha substituït per una de més nova o s'ha revocat. |
404 | not_found_error | Not Found | El camí no existeix, per exemple /v1/embeddings. |
405 | api_error | Method Not Allowed | El camí existeix però no amb aquest mètode HTTP. |
413 | invalid_request_error | Failed to buffer the request body: … | El cos de la sol·licitud és més gran que 32 MiB. |
415 | invalid_request_error | Expected request with `Content-Type: application/json` | La capçalera Content-Type falta o no és application/json. |
422 | invalid_request_error | Failed to deserialize the JSON body into the target type: … | Falta un camp obligatori, com messages, o un camp té un tipus JSON incorrecte. |
429 | rate_limit_error | Quota exceeded. Upgrade your plan at shannon-ai.com/plan | El límit de sortida de la sol·licitud és més gran que el que queda del teu saldo. |
429 | rate_limit_error | Too many requests. Retry in <n>s. | Protecció contra inundació: més de 120 sol·licituds en un minut al teu compte. <n> és el nombre de segons fins que acaba el minut. |
429 | rate_limit_error | Shannon Coder call quota reached. Upgrade your plan at shannon-ai.com/plan | shannon-coder-1 a /v1/chat/completions i /v1/messages: les crides de Shannon Coder del teu pla per a la finestra actual de 4 hores s'han esgotat. |
429 | rate_limit_error | Shannon routes are temporarily busy. Please retry. | El model està ocupat en aquest moment. |
500 | server_error | The model backend failed to answer. Please retry. | El model no ha produït cap resposta. |
500 | server_error | An internal error occurred. Please retry. | Un error intern mentre es preparava la sol·licitud. |
502 | api_error | The model backend failed to answer. Please retry. | El model no ha produït cap resposta. Família Shannon 3 i models open-weight hostejats. |
503 | api_error | Could not verify your quota right now. Please retry. | El teu saldo no s'ha pogut llegir en aquest moment. |
503 | api_error | token counting is temporarily unavailable for this model | El recompte de tokens no està disponible per a aquest model en aquest moment. |
504 | api_error | upstream timed out | Un model open-weight hostejat ha trigat més que el límit de temps d'una sol·licitud sense stream. |
Les comprovacions s'executen en aquest ordre: clau API, cos de la sol·licitud, id del model, protecció contra inundació, saldo. Una sol·licitud amb una clau incorrecta i un cos incorrecte retorna, doncs, 401. Una sol·licitud rebutjada amb un estat 4xx no es cobra.
Saldo i protecció contra inundació
Cada sol·licitud es serveix per igual: no hi ha nivells de límit de ritme ni una quota d'API separada. Tot i això, dues coses poden respondre 429, totes dues amb el tipus rate_limit_error. El missatge les distingeix.
Saldo
El teu saldo és la quota del pla d'avui, que es restableix a les 00:00 UTC, i després el teu crèdit comprat. El xat i l'API fan servir el mateix saldo.
Cada sol·licitud reserva primer el seu límit de sortida: max_tokens, o max_output_tokens a /v1/responses, 4,096 quan no envies cap dels dos. Quan la resposta és completa, la quantitat reservada se substitueix pel que la sol·licitud ha fet servir. Si la quantitat no cap en el saldo que queda, la sol·licitud retorna Quota exceeded abans que el model comenci.
Les sol·licituds que s'executen alhora reserven cadascuna la seva pròpia quantitat. Moltes sol·licituds en paral·lel amb un max_tokens alt poden, doncs, esgotar el saldo un moment, encara que cada resposta sigui curta. Un max_tokens més baix en reserva menys.
Protecció contra inundació
La protecció contra inundació permet 120 sol·licituds per minut per compte. Compta el compte, no la clau, sumant /v1/chat/completions, /v1/responses i /v1/messages. El minut comença amb la teva primera sol·licitud.
La sol·licitud número 121 dins d'aquest minut retorna Too many requests. Retry in <n>s. i no costa res. Les sol·licituds rebutjades també compten, de manera que un client que torna a enviar sense esperar continua rebutjat fins que s'acaba el minut. Espera els <n> segons del missatge.
Les sol·licituds enviades en paral·lel no es rebutgen pel fet de ser paral·leles. Esperen en cua i es responen per torn.
GET /v1/models i els endpoints de recompte de tokens no es compten.
Més sobre límits i saldo, i on veure el teu propi saldo i ús: Límits i saldo Keys & usage
Què cal tornar a provar
Torna a enviar una sol·licitud només quan la mateixa sol·licitud pot tenir èxit. Els SDK d'OpenAI i d'Anthropic ja reintenten per si mateixos dues vegades les respostes 429 i 5xx; defineix max_retries (maxRetries en JavaScript) per canviar-ho.
| Error | Tornar a enviar? | Com |
|---|---|---|
400, 404, 405, 413, 415, 422 | No. Corregeix primer la sol·licitud. | El missatge indica el camp, el model o l'entrada que és incorrecte. |
401 | No. Fes servir una clau vàlida. | Crea o substitueix la clau a la pàgina Keys & usage. |
429 Too many requests | Sí, després de l'espera. | Espera el nombre de segons del missatge i torna a enviar la mateixa sol·licitud. |
429 Quota exceeded | No tal com és. | Baixa max_tokens, espera el restabliment diari a les 00:00 UTC o afegeix crèdit. |
429 Shannon Coder call quota reached | Sí, a la finestra següent. | Les finestres comencen cada 4 hores des de les 00:00 UTC. Un altre model respon a l'instant. |
429 … temporarily busy | Sí. | Espera uns quants segons, i més a cada intent següent. |
500, 502, 503 | Sí. | Espera 1 segon, després 2, 4 i 8. Atura't després d'uns quants intents i informa de l'id de la sol·licitud. |
504 | Sí. | Envia-la amb stream definit a true: el límit de temps s'aplica a les respostes sense stream. |
Un bucle de reintents que segueix aquesta taula:
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); Errors dins d'un stream
Un stream té l'estat 200 des del primer byte. Un error posterior arriba com a esdeveniment dins del stream: un fragment amb un membre error a /v1/chat/completions, un esdeveniment error a /v1/messages, un esdeveniment response.failed o error a /v1/responses. Els esdeveniments de tancament del stream continuen arribant. Considera que aquesta resposta ha fallat i torna a enviar la sol·licitud, com amb un estat 5xx.
Una conversa més llarga que la finestra de context del model s'informa d'aquesta manera, amb el tipus invalid_request_error i el codi context_length_exceeded. Escurça la conversa abans de tornar a enviar-la.
Els fragments exactes es descriuen amb el streaming. Streaming
Ids de sol·licitud
Cada resposta, errors inclosos, té una capçalera x-request-id. És el valor que has enviat a la teva pròpia capçalera x-request-id, o 12 caràcters hexadecimals si no n'has enviat cap. Registra'l i cita'l quan demanis ajuda amb una sol·licitud.