У Qwen API есть два распространённых способа вызова: нативный интерфейс DashScope в Alibaba Cloud Model Studio и интерфейс, совместимый с OpenAI. Структуры ошибок у них различаются, но порядок диагностики один: сначала HTTP-статус, затем поля code и message в теле ответа, затем request_id, воспроизведение минимальным запросом и только после этого изменение параметров или смена модели.

Ниже разобраны 401, 404, 429, 400, 5xx и тайм-ауты: таблица кодов, поля журналов, код повторов и чек-лист перед обращением в поддержку. Модели, квоты и тарификация — по официальной документации и странице цены на модели.

Примечание: Qwen, Tongyi Qianwen, Alibaba Cloud, Bailian и DashScope — товарные знаки Alibaba Group и соответствующих правообладателей. Статья носит технический характер; мы не аффилированы с указанными компаниями, не выступаем их агентом и не состоим в партнёрских отношениях. Коды ошибок, правила лимитов и поведение интерфейсов могут меняться; ориентируйтесь на официальную документацию и фактический ответ.

1. Сначала различите режим интерфейса и структуру ошибки

Нативный интерфейс DashScope обычно возвращает code, message и request_id на верхнем уровне ответа. Совместимый с OpenAI интерфейс следует стилю OpenAI: тело ошибки выглядит как {"error": {"message": "...", "type": "...", "code": "..."}}. В режиме совместимости request_id может находиться в заголовках или в теле ответа; имя поля зависит от фактического ответа.

В коде не стоит разбирать только одну структуру. Сначала сохраните полное тело ответа в журнал как есть, затем извлекайте значение сначала из error.code, потом из code верхнего уровня. Тогда при смене режима интерфейса обработчик ошибок не придётся переписывать.

2. 401: сбой аутентификации, сначала проверьте регион

401 означает, что идентификация не прошла, и обычно не связан с возможностями модели или балансом. В Bailian чаще встречаются три ошибки:

  • InvalidApiKey или invalid_api_key: неверный ключ, неполное копирование или несовпадение Base URL и региона, к которому относится ключ.
  • invalid access token or token expired: токен истёк или используется специальный адрес тарифного плана с обычным ключом.
  • Сообщение об отказе в доступе: неверный WorkspaceId или аккаунт не входит в это рабочее пространство.

Ключ Qwen привязан к региону: ключи Пекина и Сингапура нельзя смешивать. Это самый частый пропуск: код не менялся, но после замены Base URL стабильно воспроизводится 401.

Проверяйте по порядку:

  1. Убедитесь, что заголовок — Authorization: Bearer <key> и после двоеточия есть пробел.
  2. Выведите префикс переменной окружения и проверьте, что там не пустое значение, не старое и нет перевода строки.
  3. Сверьте Base URL и регион, к которому относится ключ.
  4. Проверьте соответствие типа ключа интерфейсу: адреса для оплаты по факту, пакета Coding и пакета Token различаются.
  5. Возьмите новый ключ и выполните минимальный запрос, чтобы отделить проблему ключа от проблемы прокси или шлюза.

3. 404 и 400: имя модели, активация и контекст

404 чаще означает не сбой сервиса, а проблему с именем модели или её доступностью. При ModelNotFound или model_not_found проверьте три вещи: нет ли опечатки в имени, активирована ли модель и доступна ли она в текущем регионе. model_not_supported означает, что модель не поддерживает режим совместимости с OpenAI; нужно сменить интерфейс или модель.

Причины 400 разнообразнее. InvalidParameter — выход параметра за границы, например temperature или top_p вне диапазона; InvalidParameter.NotSupportEnableThinking — модель не поддерживает параметр режима рассуждений. Превышение контекста обычно возвращает Range of input length should be [1, xxx] с внутренним кодом InternalError.Algo.InvalidParameter; возможен и вариант, когда суммарная длина сообщений превышает предел модели. Решение — сократить историю, суммировать по частям или перейти на модель с длинным контекстом, а не повторять запрос.

4. 429: различите четыре вида лимитов и две квоты

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

HTTP Код ошибки Значение Стоит ли повторять
429 Throttling.RateQuota, LimitRequests Превышено число запросов в минуту (RPM) Да, с задержкой
429 Throttling.AllocationQuota, insufficient_quota Превышено число токенов в минуту (TPM) Да, с задержкой и сокращением входа
429 Throttling.BurstRate Кратковременный всплеск трафика вызвал защиту Да, сгладить темп
429 Throttling.Concurrency Слишком много параллельных запросов Да, снизить параллелизм
429 AllocationQuota.FreeTierOnly Бесплатная квота исчерпана, включён режим «только бесплатно» Нет, пополнить или отключить режим
429 BudgetLimitExceeded Бюджет аккаунта исчерпан Нет, скорректировать бюджет
400 Arrearage Задолженность или ненормальный статус аккаунта Нет, сначала восстановить аккаунт
403 AccessDenied.Unpurchased Сервис не активирован Нет, сначала активировать

Лимиты считаются по основному аккаунту Alibaba Cloud: вызовы RAM-субаккаунтов, рабочих пространств и нескольких ключей суммируются. Поэтому «свой ключ на каждый сервис» не обходит общую квоту, а лишь избегает взаимного вытеснения. Лимит обычно восстанавливается в течение минуты; не повторяйте запросы с высокой частотой в период ожидания.

5. Тайм-ауты и 5xx: три точки диагностики

Тайм-аут нужно разделять на три части: тайм-аут соединения обычно означает недоступность DNS, выхода или прокси; медленный первый токен — очередь у поставщика или слишком длинный вход; тайм-аут чтения — слишком длинный выход или долгий вывод. Обработка у всех разная, поэтому простое увеличение общего тайм-аута не помогает.

RequestTimeOut в Bailian означает тайм-аут запроса; официально указанный предел — 300 секунд. GatewayTimeout.InputDownload связан со скачиванием входного файла. Совместимый с OpenAI интерфейс может вернуть 503 при перегрузке движка. Если потоковый запрос прерывается без кода ошибки, сначала запишите уже полученное содержимое, последний фрагмент и причину завершения, а затем решайте, это сетевая нестабильность, разрыв прокси или тайм-аут поставщика.

6. request_id и журналы: доказательства для диагностики

Официальная документация указывает, что request_id возвращается с ответом — в заголовках или в теле. Рекомендуется вести двойную запись: клиент генерирует trace_id, который сохраняется для всех повторов, а идентификатор запроса из заголовков и request_id из тела сохраняются как есть. Если записи нет, можно найти её в журнале мониторинга моделей в консоли — при условии, что журнал вывода включён.

Журнал должен содержать как минимум: время, trace_id, Base URL, регион, имя модели, HTTP-статус, код ошибки, сообщение, request_id, длительность, число повторов и признак потокового запроса. Ключ записывайте только частично или в виде хеша; исходный текст пользователя и полный ответ не сохраняйте целиком. При обращении в поддержку одна запись с этими полями полезнее, чем «иногда возникают ошибки».

7. Повторы и задержка: пример кода

Код ниже отключает автоматические повторы SDK, повторяет только восстановимые 429 и 5xx и печатает статус, код ошибки и request_id. Задолженность, исчерпание бесплатной квоты и превышение бюджета в повторы не входят.

import os
import random
import time
import uuid

import httpx
from openai import OpenAI, APIConnectionError, APIStatusError, APITimeoutError

client = OpenAI(
    api_key=os.environ["DASHSCOPE_API_KEY"],
    base_url=os.environ.get(
        "DASHSCOPE_BASE_URL",
        "https://dashscope.aliyuncs.com/compatible-mode/v1",
    ),
    timeout=httpx.Timeout(connect=5.0, read=180.0, write=30.0, pool=10.0),
    max_retries=0,  # отключаем автоповторы SDK — управляем ими ниже
)

RETRYABLE_429 = {
    "Throttling",
    "Throttling.RateQuota",
    "Throttling.AllocationQuota",
    "Throttling.BurstRate",
    "Throttling.Concurrency",
    "LimitRequests",
    "limit_requests",
    "ResourceExhausted",
}

def unpack_error(exc: APIStatusError):
    body = {}
    try:
        body = exc.response.json()
    except Exception:
        pass
    err = body.get("error", body) if isinstance(body, dict) else {}
    code = err.get("code") if isinstance(err, dict) else None
    request_id = None
    if isinstance(body, dict):
        request_id = body.get("request_id")
    request_id = (
        request_id
        or exc.response.headers.get("x-request-id")
        or exc.response.headers.get("x-dashscope-request-id")
    )
    return code, request_id, exc.response.text[:500]

def ask(prompt: str, model: str, max_attempts: int = 4) -> str:
    trace_id = uuid.uuid4().hex[:12]
    for attempt in range(1, max_attempts + 1):
        try:
            resp = client.chat.completions.create(
                model=model,
                messages=[{"role": "user", "content": prompt}],
            )
            return resp.choices[0].message.content
        except APIStatusError as exc:
            code, request_id, body = unpack_error(exc)
            print(
                f"trace={trace_id} attempt={attempt} "
                f"http={exc.status_code} code={code} "
                f"request_id={request_id} body={body}"
            )
            retryable = exc.status_code >= 500 or (
                exc.status_code == 429 and code in RETRYABLE_429
            )
            if not retryable or attempt == max_attempts:
                raise
        except (APITimeoutError, APIConnectionError) as exc:
            print(f"trace={trace_id} attempt={attempt} err={type(exc).__name__}")
            if attempt == max_attempts:
                raise
        wait = min(2 ** (attempt - 1), 30) + random.uniform(0, 1)
        time.sleep(wait)
    raise RuntimeError("Повторы исчерпаны")

Дополнительные правила: если сервер вернул Retry-After, соблюдайте его в первую очередь; бюджет повторов должен контролироваться глобально, чтобы несколько сервисов не усиливали трафик одновременно; для запросов с побочными эффектами сначала выясните, был ли он уже выполнен, и только потом решайте, отправлять ли снова.

8. Управление параллелизмом эффективнее увеличения числа повторов

Проблему лимитов обычно решают не повторами, а управлением на входе:

  • Разделите ключи по бизнесу и задайте отдельные пределы параллелизма, чтобы одна пакетная задача не роняла онлайн-сервис.
  • Дробите пакетные задачи и разносите их по времени, избегая импульса трафика в начале часа.
  • Используйте очередь для сглаживания пиков, а неудачные задачи отправляйте в отложенную очередь, а не крутите на месте.
  • Следите за долей 429 и p95 задержки; при резком росте сначала снижайте параллелизм.
  • Для офлайн-задач по возможности используйте Batch, чтобы обойти лимиты реального времени.
  • Подготовьте резервную модель или резервный вход и до переключения проверьте совместимость параметров.

Пороги лимитов настраиваются по модели и аккаунту; пополнение не повышает RPM или TPM автоматически. Если нужен более высокий лимит, подавайте коммерческую заявку, а не копируйте чужие цифры.

9. Чек-лист перед обращением в поддержку

  1. Время и часовой пояс, а также характер сбоя: постоянный или эпизодический.
  2. Base URL, регион, путь интерфейса, имя модели и способ запроса.
  3. HTTP-статус, код ошибки и полное сообщение.
  4. request_id; если его нет — собственный trace_id.
  5. Минимальный воспроизводимый curl-запрос с обезличенным ключом.
  6. Потоковый ли запрос, длинный ли контекст, не менялась ли недавно конфигурация.
  7. Масштаб: один ключ, одно рабочее пространство или все сервисы.

10. Частые вопросы (FAQ)

1. 429 всегда означает лимит запросов?

Нет. 429 также может означать исчерпание бесплатной квоты или бюджета; задолженность может вернуть 400 Arrearage, а неактивированный сервис — 403. Сначала прочитайте код ошибки, затем решайте, ждать, пополнять или менять конфигурацию.

2. 401 сохраняется после смены ключа — что делать?

Проверяйте регион и тип ключа. Ключ Qwen привязан к региону: ключ Пекина с адресом Сингапура вернёт 401; специальный адрес тарифа с обычным ключом тоже не сработает. Затем проверьте заголовок, переменные окружения и не отбрасывает ли обратный прокси данные аутентификации.

3. Как обрабатывать превышение контекста?

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

4. Стоит ли повторять запрос при тайм-ауте?

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

5. Где искать request_id?

Сначала в заголовках и теле ответа; имя поля зависит от интерфейса. Если нигде не записано, ищите в журнале мониторинга моделей в консоли при включённом журнале вывода. В поддержку передавайте одновременно trace_id и минимальный запрос для воспроизведения.

Что дальше

Создайте тестовый ключ в ZSQH AI Gateway, отработайте по одному разу 401, 404, 429, превышение контекста и тайм-аут, затем подключите производственный мониторинг. Доступные модели и тарификация — на странице цены на модели.

Связанные статьи