NL ▾
API-sleutel ophalen

Ongecensureerde API-foutcodes en foutopsporing

Fouten zijn niet eng, maar het is eng om niet te weten of je moet herhalen of de code moet aanpassen. Deze gids breekt statuscodes één voor één af: trigger, oordeel en oplossing. Het foutlichaam is altijd JSON met een error-object met code en message. Lees eerst de body, niet alleen de statuscode. We bieden ook code voor backoff-retries, client-side rate limiting en een checklist.

Bijgewerkt op

Kernpunten

  • Alleen 429 en 503 zijn geschikt voor automatische retries; bij andere statuscodes verspil je verzoeken.
  • 402 no_credit betekent geen saldo of verlopen proefperiode, 403 content_blocked betekent geblokkeerde inhoud. Geen netwerkproblemen.
  • Gebruik exponentieel backoff met willekeurige jitter en stel een maximum aantal pogingen in.
  • 300 verzoeken per minuut per key; throttle batchtaken client-side.

Foutlichaamstructuur en leesvolgorde

Alle foutresponsen hebben dezelfde structuur:

{"error":{"code":"...","message":"..."}}

De volgorde van de stappen is vast:

  1. HTTP-statuscode bepaalt de categorie;
  2. error.code bepaalt de specifieke oorzaak; gebruik dit in code voor branching;
  3. error.message is 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

Statuscodeerror.codeBetekenisHerhalen?
400—Ongeldig verzoek, bijv. prompt plus max_tokens > 100kNee
401—Ongeldige of ontbrekende keyNee
402no_creditSaldo opgebruikt of proefperiode verlopenNee
403content_blockedInhoud geblokkeerdNee
404—Endpoint bestaat nietNee
429—Rate limit bereiktJa, backoff
503upstream_busyDienst tijdelijk drukJa, 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 Bearer prefix.
  • 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

  1. Lees de error.code in de body, niet alleen de statuscode.
  2. 401: key-prefix, omgevingsvariabelen, of is de key opnieuw gegenereerd?
  3. 400: Is de JSON geldig? Overschrijdt prompt + max_tokens de 100.000 tokens? Overschrijdt de request body 8 MB?
  4. 402: Saldo en geldigheid van de proefperiode.
  5. 403: Bevat de invoer of geschiedenis verboden inhoud?
  6. 404: Is het pad /v1/chat/completions of /v1/models?
  7. 429: Delen meerdere instanties dezelfde key? Is client-side rate limiting toegepast?
  8. 503: Is backoff retry toegepast met een interval van minimaal enkele seconden?
  9. Time-out: Is de timeout voldoende? Kan je overschakelen naar streaming?
  10. 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.

Veelgestelde vragen

Hoe lang moet je wachten na een 503 voordat je retryt?

Een paar seconden is voldoende. Wacht bij de eerste keer één tot twee seconden, verhoog daarna exponentieel en voeg willekeurige jitter toe. Stel een maximum aantal retries in.

Waarom krijg ik voortdurend een 402?

Het saldo is op of de 7-daagse proefperiode is verlopen. Na het opwaarderen van je prepaid tegoed is de toegang weer beschikbaar; het saldo verloopt niet.

Kan je een 403 content_blocked-fout omzeilen door de invoer te wijzigen?

Probeer het niet. Seksuele inhoud met minderjarigen wordt altijd geblokkeerd, ook in romans en roleplay. Gewone volwassen inhoud activeert deze fout niet.

Wordt 429 beperkt per account of per key?

De limiet is 300 verzoeken per minuut per key. Omdat elk account maar één key heeft, moeten meerdere services de rate limit samen delen.

Vul het formulier in om je key te krijgen

Maak een account aan, kopieer je key en pas de Base URL aan. Zo eenvoudig is het.