Gestione errori
Una richiesta che fallisce restituisce un codice di stato HTTP e un corpo JSON con un tipo di errore e un messaggio. Questa pagina elenca ogni stato, mostra il corpo dell'errore di ogni formato API e indica quali errori vale la pena reinviare.
Gli SDK sollevano un oggetto di errore che porta il codice di stato e i campi del corpo. Questa richiesta indica un modello che non esiste:
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"}]
}' Restituisce lo stato 400 e questo corpo:
{
"error": {
"type": "invalid_request_error",
"message": "unknown model: no-such-model"
}
} Corpo dell'errore
Ci sono due corpi. /v1/messages risponde nel formato Anthropic; ogni altro endpoint risponde nel formato OpenAI. In entrambi, i dettagli sono in error.type e error.message, quindi un solo lettore serve tutti gli endpoint.
{
"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 | Descrizione |
|---|---|---|
error.type | string | Il tipo di errore. Vedi Tipi di errore più sotto. |
error.message | string | Una frase per una persona. I testi nelle tabelle qui sotto sono stabili tranne che nelle parti variabili, indicate come <id>, <n> o …. |
error.code | string | null | Presente in alcuni errori del formato OpenAI: invalid_api_key con 401, request_too_large con 413, context_length_exceeded quando una conversazione è troppo lunga, altrimenti null o assente. Decidi prima in base allo stato e a error.type e tratta code come un extra. |
error.param | null | Presente negli stessi errori di code. Sempre null. |
type | string | Solo formato Anthropic. Sempre error. |
Tipi di errore
| Tipo | Stati | Descrizione |
|---|---|---|
invalid_request_error | 400, 413, 415, 422 | La richiesta stessa è sbagliata: un campo, il corpo, l'id del modello o un input che il modello non accetta. Modifica la richiesta prima di inviarla di nuovo. |
authentication_error | 401 | La chiave API manca, è sconosciuta o revocata. |
not_found_error | 404 | Il percorso non esiste. |
rate_limit_error | 429 | Il saldo non copre la richiesta, la flood protection l'ha fermata, oppure il modello è occupato. Il messaggio indica quale. |
api_error | 500, 502, 503, 504 | La richiesta era corretta e il guasto è dalla nostra parte. Inviala di nuovo. |
server_error | 500 | La richiesta era corretta e il guasto è dalla nostra parte. Inviala di nuovo. |
Codici di stato
| Stato | Tipo | Messaggio | Quando |
|---|---|---|---|
400 | invalid_request_error | Failed to parse the request body as JSON: … | Il corpo non è JSON valido. |
400 | invalid_request_error | unknown model: <id> | model non è un id pubblicato. Su /v1/responses anche quando model manca. |
400 | invalid_request_error | No user message provided | Livelli Shannon: la richiesta non contiene testo dell'utente. |
400 | invalid_request_error | <id> does not accept image input | È stata inviata un'immagine a un modello open-weight ospitato senza input di immagini. |
400 | invalid_request_error | audio and video input are not supported by any hosted model | È stata inviata una parte audio o video a un modello open-weight ospitato. |
400 | invalid_request_error | <id> does not accept response_format | È stato richiesto l'output strutturato a un modello open-weight ospitato che non lo prevede. |
400 | invalid_request_error | <id> does not accept response_format json_schema; use json_object | È stato inviato uno schema JSON a un modello open-weight ospitato che accetta solo json_object. |
400 | invalid_request_error | unknown reasoning effort '<value>'; expected off, low, medium or high | Lo sforzo di ragionamento non è uno dei valori elencati. Modelli open-weight ospitati. |
400 | invalid_request_error | count_tokens is available for the hosted open models; unknown model: <id> | È stato richiesto il conteggio dei token per un modello che non è tra i modelli open-weight ospitati. |
401 | authentication_error | Missing authentication | Non è stata inviata alcuna chiave API. |
401 | authentication_error | Invalid API key | La chiave è sconosciuta, è stata sostituita da una più recente oppure è stata revocata. |
404 | not_found_error | Not Found | Il percorso non esiste, per esempio /v1/embeddings. |
405 | api_error | Method Not Allowed | Il percorso esiste ma non con questo metodo HTTP. |
413 | invalid_request_error | Failed to buffer the request body: … | Il corpo della richiesta è più grande di 32 MiB. |
415 | invalid_request_error | Expected request with `Content-Type: application/json` | L'header Content-Type manca oppure non è application/json. |
422 | invalid_request_error | Failed to deserialize the JSON body into the target type: … | Manca un campo obbligatorio, come messages, oppure un campo ha il tipo JSON sbagliato. |
429 | rate_limit_error | Quota exceeded. Upgrade your plan at shannon-ai.com/plan | Il limite di output della richiesta è maggiore di ciò che resta del tuo saldo. |
429 | rate_limit_error | Too many requests. Retry in <n>s. | Flood protection: più di 120 richieste in un minuto sul tuo account. <n> è il numero di secondi fino alla fine del minuto. |
429 | rate_limit_error | Shannon Coder call quota reached. Upgrade your plan at shannon-ai.com/plan | shannon-coder-1 su /v1/chat/completions e /v1/messages: le chiamate Shannon Coder del tuo piano per la finestra di 4 ore in corso sono esaurite. |
429 | rate_limit_error | Shannon routes are temporarily busy. Please retry. | Il modello è occupato in questo momento. |
500 | server_error | The model backend failed to answer. Please retry. | Il modello non ha prodotto una risposta. |
500 | server_error | An internal error occurred. Please retry. | Un guasto interno durante la preparazione della richiesta. |
502 | api_error | The model backend failed to answer. Please retry. | Il modello non ha prodotto una risposta. Famiglia Shannon 3 e modelli open-weight ospitati. |
503 | api_error | Could not verify your quota right now. Please retry. | Non è stato possibile leggere il tuo saldo in questo momento. |
503 | api_error | token counting is temporarily unavailable for this model | Il conteggio dei token non è disponibile per questo modello in questo momento. |
504 | api_error | upstream timed out | Un modello open-weight ospitato ha impiegato più del limite di tempo di una richiesta senza streaming. |
I controlli vengono eseguiti in quest'ordine: chiave API, corpo della richiesta, id del modello, flood protection, saldo. Una richiesta con una chiave sbagliata e un corpo sbagliato restituisce quindi 401. Una richiesta rifiutata con uno stato 4xx non viene addebitata.
Saldo e flood protection
Ogni richiesta è servita allo stesso modo: non ci sono fasce di frequenza né una quota API separata. Due cose possono comunque rispondere 429, entrambe con il tipo rate_limit_error. Il messaggio le distingue.
Saldo
Il tuo saldo è la quota del piano di oggi, che si azzera alle 00:00 UTC, e dopo di essa il tuo credito acquistato. Chat e API attingono allo stesso saldo.
Ogni richiesta mette prima da parte il proprio limite di output: max_tokens, o max_output_tokens su /v1/responses, 4,096 se non ne invii nessuno. Quando la risposta è completa, la quantità messa da parte viene sostituita da ciò che la richiesta ha usato. Se la quantità non rientra nel saldo rimasto, la richiesta restituisce Quota exceeded prima che il modello inizi.
Le richieste eseguite nello stesso momento mettono ciascuna da parte la propria quantità. Molte richieste parallele con un max_tokens alto possono quindi esaurire il saldo per un momento, anche se ogni risposta è breve. Un max_tokens più basso mette da parte meno.
Flood protection
La flood protection consente 120 richieste al minuto per account. Conta l'account, non la chiave, su /v1/chat/completions, /v1/responses e /v1/messages insieme. Il minuto inizia con la tua prima richiesta.
La richiesta numero 121 all'interno di quel minuto restituisce Too many requests. Retry in <n>s. e non costa nulla. Contano anche le richieste rifiutate, quindi un client che reinvia senza attendere resta rifiutato fino al termine del minuto. Attendi i <n> secondi indicati nel messaggio.
Le richieste inviate in parallelo non vengono rifiutate perché parallele. Attendono in coda e ricevono risposta a turno.
GET /v1/models e gli endpoint di conteggio dei token non vengono conteggiati.
Altro su limiti e saldo, e dove vedere il tuo saldo e il tuo utilizzo: Limiti e saldo Chiavi e utilizzo
Cosa riprovare
Reinvia una richiesta solo quando la stessa richiesta può riuscire. Gli SDK di OpenAI e Anthropic riprovano già da soli due volte le risposte 429 e 5xx; imposta max_retries (maxRetries in JavaScript) per cambiarlo.
| Errore | Reinviare? | Come |
|---|---|---|
400, 404, 405, 413, 415, 422 | No. Correggi prima la richiesta. | Il messaggio indica il campo, il modello o l'input che è sbagliato. |
401 | No. Usa una chiave valida. | Crea o sostituisci la chiave nella pagina Chiavi e utilizzo. |
429 Too many requests | Sì, dopo l'attesa. | Attendi il numero di secondi indicato nel messaggio, poi invia la stessa richiesta. |
429 Quota exceeded | Non così com'è. | Abbassa max_tokens, attendi il reset giornaliero alle 00:00 UTC, oppure aggiungi credito. |
429 Shannon Coder call quota reached | Sì, nella finestra successiva. | Le finestre iniziano ogni 4 ore a partire dalle 00:00 UTC. Un altro modello risponde subito. |
429 … temporarily busy | Sì. | Attendi qualche secondo, poi di più a ogni ulteriore tentativo. |
500, 502, 503 | Sì. | Attendi 1 secondo, poi 2, 4 e 8. Fermati dopo alcuni tentativi e segnala l'id della richiesta. |
504 | Sì. | Inviala con stream impostato su true: il limite di tempo si applica alle risposte senza streaming. |
Un ciclo di retry che segue questa tabella:
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); Errori all'interno di uno stream
Uno stream ha stato 200 fin dal primo byte. Un errore successivo arriva come evento all'interno dello stream: un chunk con un membro error su /v1/chat/completions, un evento error su /v1/messages, un evento response.failed o error su /v1/responses. Gli eventi di chiusura dello stream seguono comunque. Considera una risposta del genere come fallita e invia di nuovo la richiesta, come per uno stato 5xx.
Una conversazione più lunga della finestra di contesto del modello viene segnalata così, con il tipo invalid_request_error e il codice context_length_exceeded. Accorcia la conversazione prima di inviarla di nuovo.
I frame esatti sono descritti nella pagina sullo streaming. Streaming
Id delle richieste
Ogni risposta, errori compresi, ha un header x-request-id. È il valore che hai inviato nel tuo header x-request-id, oppure 12 caratteri esadecimali se non ne hai inviato nessuno. Registralo nei log e citalo quando chiedi aiuto per una richiesta.