RU ▾
Получить API-ключ

Справочник по кодам ошибок и отладке API без цензуры

Ошибки — не беда, беда — не знать, стоит ли повторять запрос или менять код. В этом руководстве мы разберем каждый код состояния: условия возникновения, критерии оценки и способы исправления. Тело ошибки всегда имеет формат JSON: объект error с полями code и message. Поэтому первый шаг отладки — чтение тела ответа, а не только статуса. Ниже приведен код с экспоненциальной задержкой, код ограничения скорости на клиенте и чек-лист для пошаговой проверки.

Обновлено

Ключевые моменты

  • Автоматический повтор стоит выполнять только для 429 и 503. Повторные попытки для остальных кодов просто потратят запросы впустую.
  • 402 no_credit означает отсутствие средств или истечение пробного периода, 403 content_blocked — блокировку контента. Это не сетевые ошибки.
  • Повторные запросы должны включать экспоненциальную задержку со случайным разбросом и ограничением максимального числа попыток.
  • Лимит: 300 запросов в минуту на ключ. Для пакетных задач необходимо ограничить скорость на стороне клиента.

Формат тела ошибки и порядок чтения

Все ответы об ошибках имеют одинаковую структуру:

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

Порядок чтения фиксирован и состоит из трех шагов:

  1. HTTP-статус определяет общий класс ошибки;
  2. error.code указывает точную причину и используется в коде для ветвления;
  3. error.message предназначен для чтения человеком и ведения логов, не используйте его для строкового сравнения.

Разделите ошибки на две категории: устраняемые повторными попытками (429, 503, тайм-ауты сети) и требующие изменений (остальные). Смешивание причин — частая ошибка, например, бесконечные повторные попытки при 402 приводят к десяткам неэффективных запросов в секунду.

В логах рекомендуется фиксировать четыре поля: время, статус, error.code и оценку prompt_tokens текущего запроса. Это позволит сразу увидеть всплеск определенного типа ошибок или общий рост сбоев. Настройте алерт: 402 — при первом появлении, так как сервис недоступен; 429 — по пропорции, так как единичные случаи нормальны, а устойчивые указывают на проблемы с параллельными запросами.

Быстрая шпаргалка по кодам состояния

Статусerror.codeЗначениеПовтор?
400—Неверный запрос, например, сумма prompt и max_tokens превышает 100kНет
401—Неверный или отсутствующий ключНет
402no_creditСредства исчерпаны или пробный период истекНет
403content_blockedКонтент заблокированНет
404—Эндпоинт не найденНет
429—Превышен лимит запросовДа, с задержкой
503upstream_busyСервис временно перегруженДа, через несколько секунд

Значение «—» в таблице означает отсутствие фиксированной строки code, требующей специальной обработки; достаточно ветвления по статусу.

4xx: измените запрос, не повторяйте

400 Неверный запрос

  • Причина 1: ошибка формата JSON, часто возникает из-за неэкранированных кавычек при ручном формировании строки.
  • Причина 2: сумма токенов промпта и max_tokens превышает 100,000. Чаще всего возникает в длинных диалогах.
  • Причина 3: тело запроса превышает 8 MB.
  • Решение: используйте библиотеку JSON для сериализации; перед отправкой оценивайте количество токенов, при превышении лимита обрезайте историю или уменьшайте max_tokens; см.Практическое руководство по длинному контексту.

401 Проблемы с ключом

  • Отсутствует заголовок или префикс Bearer .
  • Ключ был перегенерирован, старый ключ стал недействительным, но на одном из серверов все еще используется старое значение.
  • Переменная окружения не передана в контейнер или в cron-задачу.

402 no_credit

Баланс исчерпан или истёк 7-дневный пробный период. Восстановите доступ, пополнив предоплаченный баланс на странице аккаунта. Мониторьте баланс самостоятельно, чтобы не ждать, пока пользователи сообщат об ошибке.

403 content_blocked

Контент заблокирован. Допустимый контент для взрослых, вымышленные и спорные темы не блокируются, но контент сексуального характера с участием несовершеннолетних блокируется всегда, включая художественную литературу и ролевые игры. При ошибке 403 проверяйте ввод и историю на наличие такого контента, а не меняйте формулировки.

404 эндпоинт не найден

Доступны только два эндпоинта: POST /v1/chat/completions и GET /v1/models. Ошибка 404 возникает, если в пути отсутствует /v1, добавлены лишние слэши, допущена опечатка или вы обращаетесь к неподдерживаемым эндпоинтам для эмбеддингов, изображений и т. д.

400: ошибка часто возникает при длинных диалогах, где количество токенов растёт линейно. Тестирование проходит успешно, но после десятков раундов появляется 400. Это не нестабильность API, а исчерпание контекстного окна. Ограничьте историю: удаляйте старые раунды или сжимайте их в резюме. Оценивайте лимиты до отправки запроса.

Совет по диагностике 401: выведите в лог первые и последние четыре символа ключа, не показывая его полностью, и сравните с тем, что отображается на странице аккаунта. Это поможет убедиться, что в процессе используется именно тот ключ, который вы ожидаете. В контейнерных средах и при выполнении периодических задач наиболее частой причиной является отсутствие переменной окружения или чтение устаревшего значения.

429 и 503: два случая, когда нужно повторить запрос

429 лимит запросов

Лимит: 300 запросов в минуту на ключ. Вызывается пакетными задачами, использованием одного ключа несколькими экземплярами или штормом повторных попыток. Решение: сначала ограничьте частоту на стороне клиента (код ниже), затем применяйте экспоненциальную задержку при повторных попытках для 429.

503 upstream_busy

Сервис временно перегружен, повторите запрос через несколько секунд. Не отправляйте запросы сразу друг за другом и не повторяйте их по десять раз в секунду — это только ухудшит ситуацию.

Тайм-аут сети

Генерация длинного текста занимает много времени, поэтому не устанавливайте слишком короткий тайм-аут; в примере указано 120 секунд. При повторном запросе после тайм-аута помните: предыдущий запрос мог уже выполниться на сервере, и повторный вызов может привести к двойному списанию средств. Для генеративных запросов количество повторных запросов после тайм-аута должно быть небольшим; лучше использовать потоковую передачу, чтобы сократить время ожидания.

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"])

Важно: формула экспоненциальной задержки — min(cap, base × 2^n) × случайный коэффициент. Случайная добавка (джиттер) предотвращает одновременный повтор запросов множеством клиентов. Статусы из набора FATAL не повторяются вообще.

Ограничение скорости на стороне клиента и диагностические запросы

Вместо того чтобы ждать 429 и затем применять задержку, лучше ограничить скорость заранее. Приведённый ниже инструмент гарантирует, что количество запросов в минуту не превысит заданное значение; он безопасен для многопоточного использования:

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))

Самый чистый способ диагностики — выполнить минимальный запрос через curl, отключив бизнес-логику. Флаг -i позволяет одновременно увидеть строку состояния и заголовки ответа:

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"}]}'

Если curl работает, а ваш код — нет, проблема в вашем коде или среде (прокси, переменные окружения, кодировка). Если curl тоже не работает, проверьте ключ, баланс и сеть.

Почему лимит установлен на 240, а не на 300: при развёртывании нескольких экземпляров каждый экземпляр ограничивает скорость самостоятельно, и их суммарная нагрузка всё равно может превысить общий лимит. Кроме того, повторные запросы занимают дополнительный квоту. Оставить 20% запаса — это надёжный подход. Если у вас несколько экземпляров, распределите общий лимит поровну между ними или используйте общий счётчик для централизованного управления.

Чек-лист по устранению неполадок

  1. Читайте значение error.code в теле ответа, а не полагайтесь только на код состояния.
  2. 401: префикс ключа, переменные окружения, не был ли ключ перегенерирован.
  3. 400: корректен ли JSON, не превышает ли сумма токенов промпта и max_tokens значение 100 000, не превышает ли тело запроса 8 МБ.
  4. 402: баланс и срок действия пробного периода.
  5. 403: не содержит ли ввод и история запрещённый контент.
  6. 404: является ли путь /v1/chat/completions или /v1/models.
  7. 429: не использует ли несколько экземпляров один ключ, применяется ли ограничение скорости на стороне клиента.
  8. 503: применялась ли экспоненциальная задержка при повторных запросх, превышал ли интервал несколько секунд.
  9. Тайм-аут: достаточно ли значение тайм-аута, можно ли переключиться на потоковую передачу.
  10. Если всё остальное исключено: воспроизведите проблему с помощью минимального запроса curl.

Если только начали подключаться, сначала ознакомьтесь со руководством по подключению. Дополнительные параметры описаны в документации.

Как пользоваться чек-листом: при возникновении проблемы последовательно исключайте варианты сверху вниз, не пропускайте пункты. Большинство «странностей» в конечном итоге сводятся к первым четырём пунктам.

Дополнительный совет: записывайте выводы по устранению неполадок в общую документацию команды. В следующий раз при аналогичной ошибке вы сможете сразу определить причину.

Часто задаваемые вопросы

Как долго ждать повторного запроса после получения 503?

Достаточно нескольких секунд. Рекомендуется ждать одну-две секунды при первом повторном запросе, затем увеличивать интервал экспоненциально и добавлять случайную добавку. Установите максимальное количество попыток.

Почему мои запросы постоянно возвращают 402?

Баланс исчерпан или истёк 7-дневный пробный период. Восстановите доступ, пополнив предоплаченный баланс. Пробный баланс не истекает.

Можно ли обойти ошибку 403 content_blocked, изменив формулировки?

Не стоит пытаться. Контент сексуального характера с участием несовершеннолетних блокируется всегда, включая художественную литературу и ролевые игры. Обычный контент для взрослых не вызывает эту ошибку.

Ограничение 429 применяется к аккаунту или к ключу?

Лимит составляет 300 запросов в минуту на каждый ключ. У каждого аккаунта только один ключ, поэтому при использовании несколькими сервисами необходимо совместно ограничивать скорость запросов.

Заполните форму, чтобы получить ключ

Создайте аккаунт, скопируйте ключ и измените Base URL. Настройка проста.