Fehler-Body-Format & Lesereihenfolge
Alle Fehlerantworten haben dieselbe Struktur:
{"error":{"code":"...","message":"..."}}
Feste Lesereihenfolge in drei Schritten:
- HTTP-Statuscode für die Kategorie;
error.codefür die genaue Ursache (Branching im Code);error.messagefür Menschen (Logs), nicht für String-Matching.
Unterscheide klar: Lösungen durch Wiederholung (429, 503, Timeout) und Lösungen durch Änderung (Rest). Vermischung ist ein häufiger Fehler, z. B. bei 402.
Logge immer vier Felder: Zeitstempel, Statuscode, error.code und geschätzte prompt_tokens. So siehst du sofort, ob eine Fehlerklasse zunimmt. Lege eine Regel fest: 402 löst sofort aus (Service nicht verfügbar); 429 nur bei hoher Rate, da gelegentliche 429 keine Behandlung benötigen.
Statuscode-Schnellreferenz
| Statuscode | error.code | Bedeutung | Retry? |
|---|---|---|---|
| 400 | — | Ungültige Anfrage, z. B. wenn prompt plus max_tokens 100k übersteigen | Nein |
| 401 | — | Ungültiger oder fehlender Key | Nein |
| 402 | no_credit | Guthaben aufgebraucht oder Testguthaben abgelaufen | Nein |
| 403 | content_blocked | Inhalt blockiert | Nein |
| 404 | — | Endpunkt nicht gefunden | Nein |
| 429 | — | Rate-Limit erreicht | Ja, Backoff |
| 503 | upstream_busy | Dienst vorübergehend ausgelastet | Ja, nach einigen Sekunden |
„—“ bedeutet: Kein fester Code-String. Branching nach Statuscode reicht.
4xx: Anfrage ändern, nicht erneut versuchen
400 Ungültige Anfrage
- Ursache 1: JSON-Fehler (fehlende Escaping-Zeichen).
- Ursache 2: prompt + max_tokens > 100.000. Häufig bei langen Dialogen.
- Ursache 3: Request-Body > 8 MB.
- Lösung: JSON-Serialisierung; Token vorab schätzen; bei Limit History kürzen oder max_tokens senken. Siehe Langer Kontext in der Praxis.
401 Schlüssel-Probleme
- Header fehlt oder
Bearer-Präfix fehlt. - Key neu generiert, alter Key sofort ungültig, aber noch im Einsatz.
- Umgebungsvariablen nicht in Container oder Cron-Jobs übernommen.
402 no_credit
Das Guthaben ist aufgebraucht oder die 7-Tage-Testphase ist abgelaufen. Gehe zurKontoseite, um dein Prepaid-Guthaben aufzuladen, um fortzufahren. Wir empfehlen, dein Service-Balance selbst zu überwachen, statt erst auf Benutzerfehler zu warten.
403 content_blocked
Inhalt blockiert. Legitime erwachsene Inhalte, fiktionale und kontroverse Themen werden nicht abgelehnt, aber sexuell explizite Inhalte mit Minderjährigen werden immer blockiert, einschließlich Romanen und Rollenspielen. Bei einem 403 solltest du Eingabe und Verlauf auf solche Inhalte prüfen, statt die Formulierung zu ändern und es erneut zu versuchen.
404 Endpunkt nicht gefunden
Es gibt nur zwei Endpunkte: POST /v1/chat/completions und GET /v1/models. Ein fehlender /v1-Pfad, ein zusätzlicher Slash, Tippfehler oder das Anfordern nicht unterstützter Endpunkte wie Embeddings oder Bilder führen zu einem 404.
Eine leicht zu missverstehende Kategorie in 400: Die Token-Anzahl bei langen Dialogen wächst linear mit den Runden. Tests tagsüber sind OK, doch nach Dutzenden Runden tritt plötzlich 400 auf. Das ist keine Instabilität, sondern das Kontextfenster ist voll. Begrenze die Historie oder komprimiere alte Inhalte. Schätze den Verbrauch vor der Anfrage ab.
Ein Trick zur Fehlersuche bei 401: Gib die ersten und letzten vier Zeichen des API-Schlüssels in die Logs aus, nicht den vollständigen Schlüssel, und vergleiche ihn mit dem auf der Kontoseite angezeigten. So stellst du sicher, dass der im Prozess gelesene Schlüssel auch wirklich der ist, den du meinst. In Container- und Cron-Job-Umgebungen sind fehlende Umgebungsvariablen oder das Lesen veralteter Werte die häufigste Ursache.
429 und 503: Zwei Fehlerklassen, die einen Retry erlauben
429 Ratenlimit
Pro API-Schlüssel sind 300 Anfragen pro Minute erlaubt. Batch-Aufgaben, mehrere Instanzen, die denselben Schlüssel teilen, oder Retry-Stürme lösen dies aus. Die Korrektur erfolgt in zwei Schichten: Client-seitiges Rate-Limiting (siehe Code weiter unten) und exponentieller Backoff bei 429.
503 upstream_busy
Der Dienst ist vorübergehend ausgelastet. Warte ein paar Sekunden und versuche es erneut. Sende nicht sofort mehrere Anfragen hintereinander und versuche auch nicht, innerhalb einer Sekunde zehn Mal zu wiederholen, das verschlimmert die Situation nur.
Netzwerk-Timeout
Die Generierung langer Texte dauert länger, setze das Timeout nicht zu niedrig an, z. B. 120 Sekunden. Achte beim Retry nach einem Timeout darauf: Die vorherige Anfrage könnte bereits auf dem Server ausgeführt worden sein, sodass ein erneuter Aufruf die Credits doppelt verbraucht. Bei Generierungsanfragen also die Retry-Anzahl gering halten und idealerweise Streaming nutzen, um die Wartezeit pro Anfrage zu verkürzen.
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"])Wichtig: Die Backoff-Formel lautet min(cap, base × 2^n) × Zufallsfaktor. Der Zufalls-Jitter verhindert, dass mehrere Clients gleichzeitig mit Retries kollidieren. Statuscodes in der FATAL-Liste werden niemals wiederholt.
Client-Rate-Limiting und Diagnose-Anfragen
Statt erst auf 429 zu warten und dann zu drosseln, solltest du im Voraus limitieren. Das folgende Tool stellt sicher, dass die Anzahl der Anfragen pro Minute unter dem eingestellten Wert bleibt, und ist threadsicher:
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))Die sauberste Methode zur Fehlersuche ist, den Anwendungscode zu verlassen und eine minimale Anfrage mit curl zu senden. Die Option -i zeigt gleichzeitig die Statuszeile und die Antwort-Header:
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"}]}'Wenn curl funktioniert, deine Anwendung aber nicht, liegt das Problem an deinem Code oder deiner Umgebung (Proxy, Umgebungsvariablen, Kodierung). Wenn auch curl fehlschlägt, prüfe den API-Schlüssel, das Guthaben und das Netzwerk.
Warum das Limit bei 240 und nicht bei 300 liegt: Bei Multi-Instance-Deployments drosseln jede Instanz separat, sodass die Summe dennoch das Gesamtlimit überschreiten kann. Hinzu kommt, dass Retries zusätzliches Kontingent verbrauchen. Eine Reserve von 20 % ist daher die sicherere Wahl. Wenn du mehrere Instanzen hast, teile das Gesamtkontingent gleichmäßig auf jede Instanz auf oder nutze einen gemeinsamen Zähler für das zentrale Scheduling.
Fehlersuche-Checkliste
- Lese den
error.codeim Body, schau nicht nur auf den Statuscode. - 401: Schlüsselpräfix, Umgebungsvariablen, ob der Schlüssel neu generiert wurde.
- 400: Ist das JSON gültig? Übersteigen prompt + max_tokens 100.000? Ist der Request-Body größer als 8 MB?
- 402: Guthaben und Gültigkeitsdauer des Testguthabens.
- 403: Enthalten Eingabe und Verlauf verbotene Inhalte?
- 404: Ist der Pfad /v1/chat/completions oder /v1/models?
- 429: Teilen mehrere Instanzen denselben API-Schlüssel? Wurde Client-Rate-Limiting implementiert?
- 503: Wurde Backoff-Retry implementiert? Ist der Abstand mindestens einige Sekunden?
- Timeout: Reicht das Timeout? Kann auf Streaming umgestellt werden?
- Wenn alles ausgeschlossen ist: Reproduziere das Problem mit einer minimalen curl-Anfrage.
Fang mit demAnleitungan. Mehr Parameter findest du in derDokumentation.
Anwendung der Checkliste: Gehe bei Problemen die Liste von oben nach unten durch, statt wild zu springen. Die meisten „seltsamen“ Fehler liegen in den ersten vier Punkten.
Ein zusätzlicher Tipp: Schreibe die Ergebnisse jeder Fehlersuche in die Teamdokumentation zurück. Bei ähnlichen Fehlern weißt du beim nächsten Mal sofort, wo du ansetzen musst.