Foutlichaamstructuur en leesvolgorde
Alle foutresponsen hebben dezelfde structuur:
{"error":{"code":"...","message":"..."}}
De volgorde van de stappen is vast:
- HTTP-statuscode bepaalt de categorie;
error.codebepaalt de specifieke oorzaak; gebruik dit in code voor branching;error.messageis voor mensen, log het maar gebruik het niet voor string-matching.
Bepaal eerst de grens: fouten die je kunt oplossen met herhaalde verzoeken (429, 503 en netwerktimeouts) en fouten die je alleen kunt oplossen door aanpassingen (de rest). Het mengen van beide is de grootste oorzaak van incidenten, zoals bij 402 constant proberen, wat leidt tot tientallen ongeldige verzoeken per seconde.
Log vier vaste velden: tijd, statuscode, error.code en de geschatte prompt_tokens van het verzoek. Zo zie je direct of een specifiek type fout toeneemt of dat het algemeen misgaat. Stel een alert in: 402 geeft direct een melding (dienst onbereikbaar); 429 alleen bij hoge frequentie (probleem in concurrency).
Statuscodes snelle referentie
| Statuscode | error.code | Betekenis | Herhalen? |
|---|---|---|---|
| 400 | — | Ongeldig verzoek, bijv. prompt plus max_tokens > 100k | Nee |
| 401 | — | Ongeldige of ontbrekende key | Nee |
| 402 | no_credit | Saldo opgebruikt of proefperiode verlopen | Nee |
| 403 | content_blocked | Inhoud geblokkeerd | Nee |
| 404 | — | Endpoint bestaat niet | Nee |
| 429 | — | Rate limit bereikt | Ja, backoff |
| 503 | upstream_busy | Dienst tijdelijk druk | Ja, na enkele seconden |
Een streepje in de tabel betekent dat er geen vaste code-string is; branch op basis van de statuscode.
4xx: pas het verzoek aan, geen retry
400 Ongeldig verzoek
- Oorzaak 1: JSON-fout, vaak door niet-geëscapete aanhalingstekens in handgeschreven strings.
- Oorzaak 2: prompt plus max_tokens > 100.000. Lange gesprekken triggeren dit vaak.
- Oorzaak 3: request body > 8 MB.
- Oplossing: serialiseer met een JSON-bibliotheek; schat tokens vooraf in en knip de history of verlaag max_tokens als het limiet wordt overschreden; zie lange context in de praktijk.
401 Key-problemen
- Header ontbreekt of mist de
Bearerprefix. - Key is geregend, maar oude key is nog in gebruik op een machine.
- Omgevingsvariabelen zijn niet meegegeven aan container of cronjob.
402 no_credit
Saldo op of 7 dagen proefperiode verlopen? Ga naar Accountpagina om prepaid tegoed op te waarderen. Monitor je saldo zelf.
403 content_blocked
Inhoud geblokkeerd. Legale volwassen inhoud, fictie en controversiële onderwerpen worden niet geweigerd, maar seksuele inhoud met minderjarigen wordt altijd geblokkeerd, inclusief fictie en roleplay. Bij een 403-fout controleer je de invoer en de geschiedenis op deze inhoud in plaats van het formulering te wijzigen en opnieuw te proberen.
404 endpoint niet gevonden
Er zijn maar twee endpoints: POST /v1/chat/completions en GET /v1/models. Een pad zonder /v1, een extra slash, een typefout of het aanroepen van niet-ondersteunde endpoints zoals embeddings of afbeeldingen resulteert in een 404.
Tokens nemen lineair toe bij lange gesprekken. Na tientallen ronden kan dit leiden tot een 400-fout. Dit komt door het contextvenster, niet door onstabiele API's. Stel een limiet in voor de historie of comprimeer oude berichten tot een samenvatting. Bereken dit al voordat je een verzoek verstuurt.
Een handige tip bij het oplossen van 401-fouten: log de eerste en laatste vier tekens van je key in je logs, niet de volledige key, en vergelijk deze met de weergave op je accountpagina. Zo controleer je of het proces de juiste key leest, vooral in container- en cron-omgevingen waar ontbrekende of verouderde omgevingsvariabelen de meest voorkomende oorzaak zijn.
429 en 503: twee foutcodes om te retryen
429 rate limit
300 verzoeken per minuut per key. Massale taken, meerdere instanties die dezelfde key delen, en retry-stormen kunnen dit triggeren. Pas twee maatregelen toe: beperk eerst de snelheid aan de clientkant (zie de code hieronder) en pas vervolgens backoff toe bij 429-fouten.
503 upstream_busy
De service is tijdelijk bezorgd; wacht een paar seconden en probeer het opnieuw. Stuur geen directe opeenvolgende verzoeken en probeer ook niet tien keer binnen één seconde te retryen, want dat maakt de situatie erger.
Netwerktime-out
Stel de timeout niet te laag in (bijv. 120s). Let op: bij een timeout is het verzoek mogelijk al verwerkt, wat leidt tot dubbele aanroepen en dubbele kosten. Vermijd herhaalde verzoeken bij tekstgeneratie en gebruik streaming om de wachttijd te verkorten.
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"])Belangrijk: de backoff-formule is min(cap, base × 2^n) × willekeurige factor. Willekeurige jitter voorkomt dat meerdere clients tegelijk proberen te retryen. Foutcodes in de FATAL-verzameling worden nooit opnieuw geprobeerd.
Client-side rate limiting en diagnostische verzoeken
In plaats van te wachten op een 429-fout en dan te backen, is het beter om vooraf te limiteren. De onderstaande hulpmiddelen garanderen dat het aantal verzoeken binnen één minuut onder de ingestelde waarde blijft, en zijn thread-safe:
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))De schoonste manier om te troubleshooten is om je businesscode te omzeilen en een minimaal verzoek te sturen met curl. De vlag -i toont zowel de statusregel als de response headers:
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"}]}'Als curl wel werkt maar je businesscode niet, ligt het probleem bij je code of omgeving (proxy, omgevingsvariabelen, codering). Werkt curl ook niet, controleer dan je key, saldo en netwerk.
Waarom 240 en niet 300? Bij meerdere instances limiteren de instances zichzelf, maar samen kunnen ze de totale limiet overschrijden. Herhaalde verzoeken gebruiken ook extra quota. Houd 20% marge aan voor een veilige configuratie. Verdeel het totale quota over je instances of gebruik een gedeelde teller voor de verdeling.
Foutopsporingslijst
- Lees de
error.codein de body, niet alleen de statuscode. - 401: key-prefix, omgevingsvariabelen, of is de key opnieuw gegenereerd?
- 400: Is de JSON geldig? Overschrijdt prompt + max_tokens de 100.000 tokens? Overschrijdt de request body 8 MB?
- 402: Saldo en geldigheid van de proefperiode.
- 403: Bevat de invoer of geschiedenis verboden inhoud?
- 404: Is het pad /v1/chat/completions of /v1/models?
- 429: Delen meerdere instanties dezelfde key? Is client-side rate limiting toegepast?
- 503: Is backoff retry toegepast met een interval van minimaal enkele seconden?
- Time-out: Is de timeout voldoende? Kan je overschakelen naar streaming?
- Als bovenstaande allemaal zijn uitgesloten, reproduceer het probleem met een minimaal curl-verzoek.
Zojuist begonnen? Lees eerst de integratietutorial. Meer parameters vind je in de documentatie.
Gebruik de checklist: werk van boven naar beneden af bij een probleem en sla stappen niet over. De meeste 'vreemde' fouten zitten in de eerste vier punten.
Extra tip: schrijf de conclusies van elke troubleshooting-sessie terug in de teamdocumentatie. Zo kun je bij dezelfde foutmelding in de toekomst direct de juiste oplossing vinden.