Formato corpo errore e ordine di lettura
Tutte le risposte di errore hanno la stessa struttura:
{"error":{"code":"...","message":"..."}}
L'ordine di lettura è fisso in tre passaggi:
- 1. Codice di stato HTTP: definisce la categoria.
error.codeindica la causa; usalo nel codice per i branch;error.messageè per gli umani: scrivilo nei log, non fare match di stringhe.
Definisci chiaramente la distinzione: cosa si risolve con il retry (429, 503, timeout di rete) e cosa richiede una modifica (tutto il resto). Mescolarle è la causa principale di incidenti, come retryare su 402 generando decine di richieste inutili al secondo.
Nei log fissa sempre: timestamp, codice stato, error.code, stima prompt_tokens. Così vedi subito picchi di errori. Imposta un alert: 402 notifica immediata (servizio non disponibile); 429 solo se il tasso è alto (problema di concorrenza).
Ricerca rapida codici di stato
| Codice di stato | error.code | Significato | Retry? |
|---|---|---|---|
| 400 | — | Richiesta non valida, es. prompt + max_tokens > 100k | No |
| 401 | — | Chiave non valida o mancante | No |
| 402 | no_credit | Saldo esaurito o prova scaduta | No |
| 403 | content_blocked | Contenuto bloccato | No |
| 404 | — | Endpoint non trovato | No |
| 429 | — | Rate limit raggiunto | Sì, backoff |
| 503 | upstream_busy | Servizio temporaneamente occupato | Sì, dopo pochi secondi |
Il simbolo “—” indica che non c'è un codice fisso da gestire; gestisci in base allo status code.
4xx: modifica la richiesta, non retryare
400 Richiesta non valida
- Causa 1: errore JSON, spesso per virgolette non escapeate nelle stringhe scritte a mano.
- Causa 2: prompt + max_tokens supera 100.000. Le conversazioni lunghe lo attivano facilmente.
- Causa 3: Corpo della richiesta > 8 MB.
- Correzione: serializza con libreria JSON; stima i token prima di inviare; se superi il limite, taglia la cronologia o riduci max_tokens; vediPratica sul contesto lungo.
401 Problemi con la chiave
- Header mancante o manca il prefisso
Bearer. - Hai rigenerato la chiave: quella vecchia è invalidata, ma una certa macchina la sta ancora usando.
- La variabile d'ambiente non è stata passata al container o al task programmato.
402 no_credit
Saldo esaurito o prova 7 giorni scaduta. Vai allapagina dell'accountper ricaricare il credito prepagato e ripristinare. Monitora il saldo del tuo servizio, non aspettare che l'utente veda un errore.
403 content_blocked
Contenuto bloccato. I contenuti per adulti legali, la finzione e i temi controversi non sono rifiutati, ma il contenuto sessuale minorile è sempre bloccato. Con 403, controlla input e cronologia, non retryare cambiando la formulazione.
404 endpoint non trovato
Solo due endpoint: POST /v1/chat/completions e GET /v1/models. Errori 404 se manca /v1, ci sono slash extra, il path è sbagliato o chiami embeddings/image non supportati.
Alcuni 400 sono ingannevoli: i token crescono linearmente con le conversazioni lunghe. Di giorno i test vanno bene, ma dopo decine di round arrivano 400. Non è instabilità, è il contesto al limite. Imposta un max alla cronologia o compatta i vecchi messaggi. Stima prima di inviare.
Un trucco per il debug degli errori 401: stampa nei log le prime e le ultime quattro cifre della chiave, non il contenuto completo, e confrontale con quelle visibili nella pagina dell'account. Questo ti permette di confermare se il processo sta leggendo effettivamente la chiave che credi, soprattutto in ambienti containerizzati o di pianificazione, dove la causa più comune è la mancanza o la lettura di valori vecchi delle variabili d'ambiente.
429 e 503: le due classi da ritentare
Limite di richieste 429
300 richieste al minuto per chiave. Batch, chiavi condivise o tempeste di retry le attivano. Soluzione: prima limiti di richiesta lato client, poi retry con backoff sui 429.
503 upstream_busy
Il servizio è temporaneamente occupato; riprova dopo qualche secondo. Non inviare richieste consecutive immediatamente e non ritentare dieci volte in un secondo, perché peggioreresti la situazione.
Timeout di rete
La generazione di testo lungo richiede tempo: imposta un timeout di 120 s. Attenzione ai retry: la richiesta potrebbe essere già stata eseguita su una certa macchina, consumando credito due volte. Limita i retry per le richieste di generazione e usa lo streaming.
import os
import random
import time
import requests
URL = "https://api.wushenchaapi.com/v1/chat/completions"
HEADERS = {
"Authorization": "Bearer " + os.environ["API_KEY"],
"Content-Type": "application/json",
}
RETRYABLE = {429, 503} # 只重试这两类
FATAL = {400, 401, 402, 403, 404}
class ApiError(Exception):
def __init__(self, status, code, message):
super().__init__(f"{status} {code}: {message}")
self.status, self.code = status, code
def call(payload, max_retries=5, base=1.0, cap=30.0):
for attempt in range(max_retries + 1):
try:
r = requests.post(URL, headers=HEADERS, json=payload, timeout=120)
except (requests.ConnectionError, requests.Timeout):
r = None # 网络层失败,按可重试处理
if r is not None and r.ok:
return r.json()
if r is not None:
try:
err = r.json().get("error", {})
except ValueError:
err = {}
if r.status_code in FATAL or r.status_code not in RETRYABLE:
raise ApiError(r.status_code, err.get("code"), err.get("message"))
if attempt == max_retries:
raise ApiError(r.status_code if r is not None else 0, "retry_exhausted", "重试次数用尽")
delay = min(cap, base * (2 ** attempt)) * random.uniform(0.5, 1.0)
time.sleep(delay)
if __name__ == "__main__":
out = call({"model": "uncensored", "max_tokens": 100,
"messages": [{"role": "user", "content": "回复一个字:好"}]})
print(out["choices"][0]["message"]["content"])La formula del backoff è min(cap, base × 2^n) × coefficiente casuale. Il jitter evita collisioni tra client. I codici FATAL non vengono retryati mai.
Limitazione lato client e richieste di diagnostica
Invece di aspettare il 429 per applicare il backoff, è meglio limitare le richieste in anticipo. Il seguente strumento garantisce che il numero di richieste al minuto rimanga sotto il valore impostato ed è sicuro per il multithreading:
import threading
import time
class RateGate:
"""简单的客户端限速:保证一分钟内请求数不超过 limit。"""
def __init__(self, limit=240): # 留出余量,低于 300/分钟
self.limit, self.stamps, self.lock = limit, [], threading.Lock()
def wait(self):
while True:
with self.lock:
now = time.time()
self.stamps = [t for t in self.stamps if now - t < 60]
if len(self.stamps) < self.limit:
self.stamps.append(now)
return
sleep_for = 60 - (now - self.stamps[0])
time.sleep(max(sleep_for, 0.05))Per il debug, il metodo più pulito è isolarsi dal codice di business e inviare una richiesta minima con curl. L'opzione -i mostra contemporaneamente la riga di stato e le intestazioni di risposta:
curl -i https://api.wushenchaapi.com/v1/chat/completions \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"uncensored","max_tokens":20,"messages":[{"role":"user","content":"ping"}]}'Se curl funziona ma il codice di business no, il problema è nel tuo codice o nell'ambiente (proxy, variabili d'ambiente, codifica). Se anche curl fallisce, controlla la chiave, il saldo e la rete.
Perché impostare il limite a 240 invece di 300: in caso di distribuzione multi-istanza, ogni istanza applica il proprio limite e la somma può comunque superare il totale; inoltre i ritentativi consumano quota aggiuntiva. Lasciare un margine del 20% è una scelta prudente. Se hai più istanze, dividi equamente la quota totale tra ciascuna istanza o utilizza un contatore condiviso per la gestione centralizzata.
Checklist di troubleshooting
- Leggi
error.codenel body, non affidarti solo allo stato HTTP. - 401: prefisso della chiave, variabili d'ambiente, verifica se è stata rigenerata.
- 400: il JSON è valido? prompt + max_tokens superano 100.000? Il corpo della richiesta supera 8 MB?
- 402: saldo e validità del periodo di prova.
- 403: input e cronologia contengono contenuti proibiti?
- 404: il percorso è /v1/chat/completions o /v1/models?
- 429: più istanze condividono la stessa chiave? È stata applicata una limitazione lato client?
- 503: è stato applicato il backoff esponenziale? L'intervallo è di almeno qualche secondo?
- Timeout: il timeout è sufficiente? Puoi passare allo streaming?
- Se tutto è escluso: riproduci il problema con una richiesta minima via curl.
Se ti stai appena connettendo, consulta prima la guida all'integrazione. Per i parametri avanzati, consulta la documentazione.
Usa la checklist: escludi i problemi dall'alto verso il basso, senza saltare. La maggior parte dei problemi
Un consiglio extra: scrivi le conclusioni del troubleshooting nella documentazione del team. In futuro, lo stesso errore ti permetterà di identificare immediatamente la causa.