Qwen API 有两套常见调用方式:阿里云百炼的原生 DashScope 接口,以及 OpenAI 兼容接口。两者的错误结构不完全一样,但排查顺序一致:先看 HTTP 状态码,再读响应体里的 code 和 message,然后找 request_id,用最小请求复现,最后才改参数或换模型。

本文按 401、404、429、400、5xx 和超时拆开,给出错误码对照、日志字段、重试代码和联系支持前的清单。模型、配额与计费以官方文档和站内模型价格为准。

说明:Qwen、通义千问、阿里云、百炼、DashScope 是阿里巴巴集团及相关权利人的商标。本文仅作技术排查讨论,与上述公司无隶属、代理或合作关系。错误码、限流口径与接口行为可能随版本调整,最终以官方文档和实际响应为准。

一、先分清接口模式与错误结构

原生 DashScope 接口通常在响应顶层返回 code、message 和 request_id;OpenAI 兼容接口沿用 OpenAI 风格,错误体形如 {"error": {"message": "...", "type": "...", "code": "..."}}。兼容模式的 request_id 可能出现在响应头或响应体中,字段名以实际返回为准。

工程上不要只解析一种结构。先把完整响应体原样记进日志,再按 error.code、顶层 code 的顺序取值。这样切换接口模式时,错误处理代码不用重写。

二、401:鉴权失败,先查区域

401 表示身份没有通过验证,和模型能力、余额通常无关。百炼常见的三类错误是:

  • InvalidApiKey 或 invalid_api_key:Key 错误、复制不完整,或 Base URL 与 Key 所属区域不一致。
  • invalid access token or token expired:Token 已过期,或用了套餐专属地址却配了普通 Key。
  • NOT AUTHORIZED:WorkspaceId 无效,或账号不在该工作空间。

Qwen 的 API Key 与区域绑定,北京和新加坡的 Key 不能混用。这是最容易漏掉的一条:代码没改,只换了 Base URL,就会稳定复现 401。

排查按顺序做:

  1. 确认请求头是 Authorization: Bearer <key>,冒号后有一个空格。
  2. 打印环境变量前缀,确认不是空值、旧值或带换行。
  3. 核对 Base URL 与 Key 的所属区域是否一致。
  4. 确认 Key 类型与接口匹配:按量付费、Coding 套餐、Token 套餐的地址不同。
  5. 换一把新 Key 做最小请求,区分 Key 问题与代理、网关问题。

三、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,也可能是总消息长度超过模型上限。处理方式是裁剪历史、分块摘要或改用长上下文型号,而不是反复重试。

四、429:先分清四种限流和两种额度

429 是最容易被误处理的错误。百炼的 429 至少包含以下几类:

HTTP 错误码 含义 是否适合重试
429 Throttling.RateQuota、LimitRequests 每分钟请求数(RPM)超限 是,退避后重试
429 Throttling.AllocationQuota、insufficient_quota 每分钟 Token 数(TPM)超限 是,退避并降低输入
429 Throttling.BurstRate 短时流量突增触发保护 是,平滑发送节奏
429 Throttling.Concurrency 并发请求过多 是,降低并发
429 AllocationQuota.FreeTierOnly 免费额度用尽且开启了仅用免费额度 否,充值或关开关
429 BudgetLimitExceeded 账户预算已用完 否,调整预算
400 Arrearage 账户欠费或状态异常 否,先恢复账户
403 AccessDenied.Unpurchased 服务未开通 否,先开通

限流按阿里云主账号统计,RAM 子账号、工作空间和多个 Key 的调用会合并计算。所以“每个服务一把 Key”并不能绕过总额度,只能避免互相挤占。限流通常在分钟级恢复,等待期间不要高频重试。

五、超时与 5xx:分三段定位

超时要分三段看:连接超时通常是 DNS、出口或代理不可达;首 Token 慢多半是上游排队或输入过长;读取超时则可能是输出太长或推理时间久。三者的处理方式不同,不能只把超时时间调大。

百炼的 RequestTimeOut 表示请求超时,官方说明的请求时限为 300 秒;GatewayTimeout.InputDownload 与输入文件下载有关。OpenAI 兼容接口还可能返回 503,表示引擎过载。流式请求中断时,可能没有错误码,先记录已接收内容、最后一段数据和结束原因,再判断是网络抖动、代理断流还是上游超时。

六、request_id 与日志:排障的凭证

官方说明 request_id 会随响应返回,可能在响应头或响应体中。工程上建议双轨记录:客户端生成 trace_id 贯穿所有重试,同时原样保存响应头里的请求标识和响应体里的 request_id。若没有记录,可在控制台的模型监控日志中查找,前提是已开启推理日志。

日志至少包含:时间、trace_id、Base URL、区域、模型名、HTTP 状态码、错误码、错误信息、request_id、耗时、重试次数、是否流式。Key 只记录前几位或哈希,用户原文和完整响应不要整段落盘。联系支持时,一条带这些字段的记录比“偶尔报错”有用得多。

七、重试与退避:示例代码

下面这段代码关闭 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")
    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 时优先遵守;重试预算要全局控制,避免多个服务同时放大流量;有副作用的请求先确认是否已执行,再决定是否重发。

八、并发治理:比调大重试更有效

限流问题通常不是靠重试解决的,而是靠入口治理:

  • 按业务拆分 Key,并分别设置并发上限,避免一个批量任务拖垮在线服务。
  • 批量任务分片、错峰,避免整点流量脉冲。
  • 用队列削峰,失败任务进入延迟队列,而不是原地打转。
  • 监控 429 占比和 p95 延迟,突增时先降并发。
  • 离线任务优先用 Batch 接口,避开实时限流。
  • 准备备用模型或备用入口,切换前确认参数兼容。

限流阈值按模型和账号配置,充值不会自动提高 RPM 或 TPM。需要更高额度时,走商务申请,不要照搬别人的数字。

九、联系支持前的清单

  1. 发生时间、时区,以及故障是否集中爆发。
  2. Base URL、区域、接口路径、模型名和请求方式。
  3. HTTP 状态码、错误码和完整错误信息。
  4. request_id;没有就提供自己生成的 trace_id。
  5. 一条可复现的最小 curl 请求,Key 做脱敏。
  6. 是否流式、是否长上下文、是否刚改过配置。
  7. 影响范围:单 Key、单工作空间还是全部服务。

十、常见问题(FAQ)

1. 429 一定是限流吗?

不一定。429 还可能是免费额度用尽、预算超限;欠费可能返回 400 的 Arrearage,服务未开通可能返回 403。先读错误码,再决定是退避、充值还是改配置。

2. 401 换了新 Key 还是失败怎么办?

重点查区域和 Key 类型。Qwen 的 Key 与区域绑定,北京 Key 配新加坡地址会返回 401;套餐专属地址配普通 Key 也会失败。再检查请求头、环境变量和反向代理是否丢弃认证信息。

3. 上下文超限怎么处理?

先裁剪历史消息,去掉无关附件和重复段落;再把长文档分块摘要或做检索;仍不够时改用长上下文型号。不要靠提高 max_tokens 解决输入超限。

4. 超时应该重试吗?

只对连接失败和读取超时做有限次重试,并设置指数退避和上限。有副作用的业务先确认是否执行过,避免重复下单或重复写入。

5. request_id 在哪里找?

先看响应头和响应体,字段名以实际接口为准;如果都没有记录,再到控制台的模型监控日志中查找,前提是已开启推理日志。联系支持时同时提供 trace_id 和最小复现请求。

下一步

先在中商起航 AI 网关创建测试 Key,把 401、404、429、上下文超限和超时各演练一次,再接入生产监控。可用模型与计费方式见模型价格。

相关阅读