Formato do corpo de erro e ordem de leitura
Todas as respostas de falha têm a mesma estrutura:
{"error":{"code":"...","message":"..."}}
A ordem de leitura é fixa em três etapas:
- Código de status HTTP, que define a categoria;
error.code, que define a causa específica e deve ser usado para ramificação no código;error.message, para leitura humana e logs; não use para correspondência de strings.
Defina o limite: solucionáveis com tentativas (429, 503, timeout) e que exigem alteração (demais). Misturá-los causa falhas, como tentar novamente para 402.
Registre fixamente quatro campos nos logs: timestamp, código de status, error.code e a estimativa de prompt_tokens da requisição. Isso permite identificar rapidamente se um tipo de erro aumentou ou se as falhas são gerais. Configure um alerta: notifique imediatamente ao receber 402 (serviço indisponível); para 429, analise a proporção — esporádico é normal, contínuo indica problema de concorrência.
Consulta rápida de códigos de status
| Código de status | error.code | Significado | Tentar novamente? |
|---|---|---|---|
| 400 | — | Requisição inválida, como prompt + max_tokens ultrapassando 100k | Não |
| 401 | — | Chave inválida ou ausente | Não |
| 402 | no_credit | Saldo esgotado ou crédito de teste expirado | Não |
| 403 | content_blocked | Conteúdo bloqueado | Não |
| 404 | — | Endpoint não encontrado | Não |
| 429 | — | Limite de requisições excedido | Sim, com backoff |
| 503 | upstream_busy | Serviço ocupado temporariamente | Sim, após alguns segundos |
O traço “—” na tabela indica que não há um código fixo específico para tratamento; use o código de status para ramificação.
4xx: corrija a requisição, não tente novamente
400 Requisição inválida
- Causa 1: Erro de formatação JSON, comum quando strings são montadas manualmente sem escapar as aspas.
- Causa 2: prompt + max_tokens excede 100.000. Diálogos longos são os mais propensos a isso.
- Causa 3: Corpo da requisição excede 8 MB.
- Correção: use uma biblioteca JSON para serialização; estime tokens antes de enviar e, se exceder, corte o histórico ou reduza max_tokens; veja Prática de contexto longo.
401 Problema com a chave
- Header ausente ou falta o prefixo
Bearer. - A chave foi regenerada, invalidando a antiga imediatamente, mas algum servidor ainda usa o valor antigo.
- Variáveis de ambiente não foram passadas para o contêiner ou para tarefas agendadas.
402 sem_crédito
O saldo acabou ou o período de teste de 7 dias expirou. Vá para a página da conta para recarregar o crédito pré-pago e restaurar o acesso. Recomendamos que você monitore o saldo do seu serviço para não descobrir o problema apenas quando o usuário reportar um erro.
403 conteúdo_bloqueado
Conteúdo bloqueado. Conteúdo adulto legítimo, ficção e tópicos controversos não são rejeitados, mas conteúdo sexual envolvendo menores é sempre bloqueado, incluindo ficção e roleplay. Ao receber 403, verifique se há esse tipo de conteúdo no input ou no histórico; não adianta apenas reescrever e tentar novamente.
404 endpoint não encontrado
Existem apenas dois endpoints: POST /v1/chat/completions e GET /v1/models. Um caminho sem /v1, barra extra, erro de digitação ou o uso de endpoints não suportados como embeddings ou imagens resultarão em 404.
Outra classe de erros 400 fácil de mal interpretar: o número de tokens em conversas longas cresce linearmente com as rodadas. Tudo funciona bem durante os testes diurnos, mas após dezenas de rodadas, erros 400 começam a aparecer. Isso não é instabilidade da API, mas sim o limite da janela de contexto. A solução é limitar o histórico: descarte as rodadas mais antigas quando ultrapassar um limite ou resuma o conteúdo antigo. Não espere o erro para agir; estime o consumo antes de enviar a requisição.
Uma dica para investigar erros 401: imprima nos logs os quatro primeiros e os quatro últimos caracteres da chave, sem exibir o conteúdo completo, e compare com o que aparece na página da conta. Isso confirma se o processo está lendo a chave correta, especialmente em ambientes de contêiner ou tarefas agendadas, onde a ausência de variáveis de ambiente ou a leitura de valores antigos são causas comuns.
429 e 503: os dois casos para retry
429 limite de requisições
Limite de 300 requisições por minuto por chave. Tarefas em lote, múltiplas instâncias compartilhando uma única chave ou tempestades de retry podem acionar o limite. A correção tem duas camadas: limite de taxa no cliente (veja o código abaixo) e retry com backoff para 429.
503 upstream_busy
Serviço temporariamente ocupado. Aguarde alguns segundos e tente novamente. Não envie requisições em cascata imediatamente nem tente dez vezes em um segundo, pois isso piora a situação.
Tempo limite de rede
A geração de texto longo leva mais tempo; não defina o timeout muito baixo, use 120 segundos como exemplo. Ao fazer retry após timeout, lembre-se: a requisição anterior pode já ter sido executada no servidor, e chamadas duplicadas podem consumir saldo duas vezes. Para requisições de geração, reduza o número de retries e prefira streaming para encurtar o tempo de espera.
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"])Resumo: a fórmula de backoff é min(cap, base × 2^n) × fator aleatório; o jitter aleatório evita que múltiplos clientes tentem retry simultaneamente. Os códigos de status na lista FATAL não devem ser retryados.
Limite de taxa no cliente e requisições de diagnóstico
Em vez de esperar o 429 para aplicar backoff, aplique limite de taxa antecipadamente. A ferramenta abaixo garante que o número de requisições por minuto fique abaixo do valor definido, sendo segura para múltiplas threads:
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))Para diagnóstico, a abordagem mais limpa é isolar o código de negócio e enviar uma requisição mínima via curl. Use -i para ver simultaneamente a linha de status e os cabeçalhos de resposta:
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 o curl funciona mas o código de negócio falha, o problema está no seu código ou ambiente (proxy, variáveis de ambiente, codificação). Se o curl também falha, verifique a chave, o saldo e a rede.
Por que o limite é 240 e não 300? Em implantações com múltiplas instâncias, cada instância aplica seu próprio limite, mas a soma pode exceder o total. Somado ao consumo extra de retries, é mais seguro deixar uma margem de 20%. Se você tem múltiplas instâncias, divida o limite total igualmente entre elas ou use um contador compartilhado para gerenciar o tráfego.
Lista de verificação de solução de problemas
- Leia o campo
error.codeno body, não confie apenas no código de status HTTP. - 401: verifique o prefixo da chave, as variáveis de ambiente e se a chave foi regenerada.
- 400: verifique se o JSON é válido, se o prompt + max_tokens excede 100.000 tokens e se o corpo da requisição excede 8 MB.
- 402: verifique o saldo e a validade do período de teste.
- 403: verifique se há conteúdo proibido no input ou no histórico.
- 404: verifique se o caminho é /v1/chat/completions ou /v1/models.
- 429: verifique se múltiplas instâncias estão compartilhando uma única chave e se o limite de taxa no cliente está configurado.
- 503: verifique se o retry com backoff está implementado e se o intervalo é de pelo menos alguns segundos.
- Timeout: verifique se o timeout é suficiente e se é possível usar streaming.
- Se tudo mais falhar, reproduza o problema com uma requisição mínima via curl.
Se você acabou de começar, consulte primeiro o guia de integração. Mais parâmetros estão na documentação.
Como usar a lista: elimine itens de cima para baixo ao enfrentar problemas, não pule etapas. A maioria das falhas 'estranhas' acaba caindo nos quatro primeiros itens.
Dica adicional: registre as conclusões de cada solução de problemas na documentação da equipe. Na próxima vez que o mesmo erro ocorrer, você poderá identificar o problema imediatamente.