GLM API 的报错有两层:外层是 HTTP 状态码,内层是响应体里的业务码。只看外层很容易误判,例如账户欠费返回的也是 429。排查顺序应当是:先读业务码和错误信息,再找请求 ID 或错误 ID,然后用最小请求复现,最后才改参数或换模型。

本文按 401、403、404、429、5xx 和超时拆开,给出业务码对照表、错误处理代码和联系支持前的清单。模型与价格以站内模型价格为准。

说明:智谱、GLM、BigModel 是相关权利人的商标,本文仅作技术说明,与智谱无隶属或代理关系。错误码、限流口径和接口行为可能随版本调整,最终以官方文档与实际响应为准。

一、先分清两层错误

HTTP 状态码说明协议层结局,业务码说明具体原因。同一个 401,可能是没带认证头、Token 过期或二次认证;同一个 429,可能是限流、模型繁忙、欠费或套餐上限。把业务码打进日志,是排障效率最高的一步。

HTTP 常见业务码 含义 是否适合重试
401 1000 / 1001 / 1003 / 1005 鉴权失败、缺认证头、Token 过期、二次认证 否,先修凭证
403 1220 无权访问该 API 或模型 否,查套餐与权限
400 1210 / 1211 / 1221 / 1222 / 1261 参数错误、模型不存在、API 下线或不存在、Prompt 超长 否,改请求
429 1113 账户欠费 否,先充值
429 1302 / 1305 速率限制、模型访问量过大 是,退避后重试
429 1308 / 1310 周期上限,按 next_flush_time 重置 等重置,不硬重试
429 1311 / 1313 / 1315 套餐无模型权限、公平使用限制、Key 类型不匹配 否,查套餐与 Key
500 1200 / 1230 / 1234 内部错误、流程出错、网络错误(含错误 ID) 是,退避并保留错误 ID

表里是最常见映射,完整列表以官方文档为准。流式中断可能没有业务码,原因体现在 finish_reason 或最后一段数据里。

二、401:四种鉴权失败

401 表示身份没有通过验证,与模型能力、余额通常无关。官方错误码把常见原因分得比较细:

  • 1000 身份验证失败:Key 错误、复制不完整,或已被删除。
  • 1001 请求头缺少认证参数:没有 Authorization: Bearer <key>,或被代理丢弃。
  • 1003 Token 已过期:Key 有有效期,或已在控制台轮换。
  • 1005 已开启二次认证:按平台要求完成验证。

排查按顺序做:

  1. 确认请求头格式:冒号后一个空格,Key 前后无引号或换行。
  2. 打印环境变量前缀,确认不是空值或旧值。
  3. 确认 Key 类型:标准模型 API、Coding Plan、团队或企业 Key 分开管理,不能混用。
  4. 确认 Base URL 与 Key 归属一致,官方 Key 配官方地址,网关 Key 配网关地址。
  5. 换一把新 Key 做最小请求,区分 Key 问题与代理、网关配置问题。

从聊天窗口复制 Key 最容易带入空格或换行。先做一次只输出一个词的请求,能排除大部分低级错误。

三、403 与 404:权限、路径和模型名

403 常见业务码是 1220,表示当前账号无权访问该 API 或模型。先检查套餐是否包含该模型、Key 是否属于对应产品类型,再检查是否调用了未开通的接口。这类错误重试无用,改权限或换 Key。

404 多数不是“服务挂了”,而是地址不对:Base URL 多了或少了 /v1,SDK 自动拼接后又手写了一遍完整路径,反向代理重写规则把路径改坏。GLM 的模型名错误通常返回 400 的 1211,API 下线或不存在对应 1221 和 1222。所以看到 404 先查地址,看到 1211 再查模型名。

模型名以官方文档和站内价格页为准,不要凭记忆写版本号。升级模型时先在测试环境确认参数兼容。

四、429:先读业务码,再决定要不要重试

429 是最容易被误处理的错误。它至少包含五类情况:

  • 1113 账户欠费:充值前重试没有意义。
  • 1302 达到速率限制:降低并发或退避。
  • 1305 模型当前访问量过大:通常与高峰有关,退避后重试,必要时切备用模型。
  • 1308、1310 达到周期使用上限:错误信息里有重置时间,等额度恢复。
  • 1309、1311、1313、1315:套餐到期、模型未开放、公平使用限制或 Key 类型不匹配,改配置才有用。

只有 1302、1305 这类瞬时限流适合自动重试,其余情况重试只会拉长故障时间。限流口径(并发、每分钟请求数或 Token 速率)随套餐和模型变化,以控制台和官方文档为准。

五、5xx 与超时:分开定位

500 系列包括 1200 内部错误、1230 流程出错、1234 网络错误。1234 通常带错误 ID,联系支持时务必保留。持续 5xx 先退避,再查服务状态或服务方通知,不要高频重试。

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

流式请求中断时,可能既没有 HTTP 错误码,也没有业务码。先记录已接收内容和最后一段数据,检查 finish_reason,再判断是网络抖动、代理断流还是上游超时。半截内容不要直接当完整结果入库。

六、请求 ID 与日志:排障的凭证

官方响应不一定在每个接口都提供同名请求 ID,字段名以实际响应和文档为准。工程上更稳妥的做法是双轨记录:客户端生成 trace_id,贯穿所有重试;同时原样保存上游返回的错误体和其中任何 request id、error id 字段。

日志至少包含:时间、trace_id、Base URL、模型名、HTTP 状态码、业务码、错误信息、耗时、重试次数、是否流式。Key 只记录前几位或哈希,用户原文和完整响应不要整段落盘。

这些字段不只是给自己看的。联系支持时,一条带 trace_id、业务码和最小复现请求的记录,比一句“偶尔报错”有用得多。

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

下面这段代码关闭 SDK 自动重试,按业务码决定是否重试,并把 trace_id、状态码和业务码一起打印。网络超时单独处理,重试有上限。

import os
import random
import time
import uuid

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

client = OpenAI(
    api_key=os.environ["GLM_API_KEY"],
    base_url=os.environ.get(
        "GLM_BASE_URL", "https://open.bigmodel.cn/api/paas/v4"
    ),
    timeout=httpx.Timeout(connect=5.0, read=120.0, write=30.0, pool=10.0),
    max_retries=0,  # 关闭 SDK 自动重试,统一由下面的逻辑控制
)

RETRYABLE_CODES = {1302, 1305}  # 速率限制、模型繁忙;欠费和套餐上限不在其中

def ask(prompt: 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=os.environ["GLM_MODEL"],
                messages=[{"role": "user", "content": prompt}],
                max_tokens=1024,
            )
            return resp.choices[0].message.content
        except APIStatusError as exc:
            code = None
            try:
                code = int(exc.response.json().get("code"))
            except Exception:
                pass
            print(
                f"trace={trace_id} attempt={attempt} "
                f"http={exc.status_code} code={code} "
                f"body={exc.response.text[:500]}"
            )
            retryable = exc.status_code >= 500 or (
                exc.status_code == 429 and code in RETRYABLE_CODES
            )
            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), 20) + random.uniform(0, 1)
        time.sleep(wait)
    raise RuntimeError("重试次数已用完")

补充三条规则:优先尊重服务端返回的 Retry-After(如果有);重试预算要全局控制,避免多个服务叠加;有副作用的请求先用幂等键确认状态,再决定是否重发。

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

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

  • 每个服务设独立 Key 和并发上限。
  • 批量任务分片、错峰,避免整点流量脉冲。
  • 用队列削峰,失败任务进入延迟队列,而不是原地打转。
  • 监控 429 占比和 P95 延迟,突增时先降并发。
  • 准备备用模型或备用入口,但切换前确认参数兼容。

并发上限、每分钟请求数和 Token 速率的具体口径随套餐与模型变化,以控制台和官方文档为准。不要照搬别人的数字,按自己的实测结果设置。

九、联系支持前的清单

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

把这几项准备齐,支持团队才能直接定位,而不是来回要信息。每次故障后补监控和告警,不要等用户反馈才发现。

十、常见问题(FAQ)

1. 429 一定是限流吗?

不一定。429 还可能是欠费(1113)、周期上限(1308、1310)、套餐无模型权限(1311)或公平使用限制(1313)。先读业务码,再决定是退避、充值还是改配置。

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

检查请求头是否为 Authorization: Bearer <key>、环境变量是否加载、Key 类型与接口是否匹配、Base URL 与 Key 归属是否一致,以及反向代理有没有丢弃认证头。

3. 超时应该重试吗?

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

4. 流式输出中途断了怎么排查?

记录已接收内容、最后一段数据和 finish_reason,区分网络抖动、代理断流和上游超时。客户端要支持重连与去重,不要把半截内容入库。

5. 报错信息里只有状态码没有业务码怎么办?

保留完整响应体和响应头,记录 trace_id、Base URL、模型名和最小复现请求,再联系服务方。走网关时也向网关方提供这些信息,便于对照上游日志。

下一步

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

相关阅读