错误处理
GLM 5 API 错误码速查表:每个错误码对应的 HTTP 状态与含义、哪些值得重试、退避策略怎么设、流式过程中的报错如何处理,以及上下文持续增长导致失败时如何排查。
非流式错误使用 OpenAI 兼容的错误对象:
{
"error": {
"message": "Invalid API key provided.",
"type": "invalid_request_error",
"code": "invalid_api_key",
"param": null
}
}
这也是多数 OpenAI 兼容 SDK 能直接识别的结构。应用逻辑优先读取 error.code,重试决策同时结合 HTTP 状态码。
错误码参考
| HTTP | Code | 含义 | 建议重试? |
|---|---|---|---|
| 400 | invalid_request_error | JSON、messages、工具参数或工具结果顺序不合法。 | 否 |
| 400 | context_length_exceeded | 请求超过了所选模型的上下文窗口,或 max_completion_tokens 超出模型本身能支持的范围——GLM 5 原样转发,只是把模型自己的拒绝结果转出来。 | 否 |
| 400 | unsupported_parameter | 当前模型不支持请求中的某项能力。 | 否 |
| 401 | invalid_api_key | Bearer Key 缺失、无效、已停用或已删除。 | 否 |
| 402 | insufficient_quota | 「可用于 Public API」的积分无法覆盖预占。 | 否 |
| 404 | model_not_found | 模型 ID 不在公开模型列表中。 | 否 |
| 413 | request_too_large | 请求体超过 4 MB。 | 否 |
| 429 | rate_limit_exceeded | API Key 超过每分钟请求限制,或上游 Provider 自己对这次请求限流了。 | 是 |
| 429 | daily_credit_limit_exceeded | 该 Key 在 /settings/apikeys 里配置的消费上限,在当前周期内已用完。 | 是,reset_at 之后 |
| 500 | internal_error | GLM 5 API 出现临时内部错误。 | 是 |
| 503 | service_unavailable | 公共 API 暂时不可用。 | 是 |
这张表覆盖的是响应开始前就返回的错误。stream: true 的请求一旦开始推流,就不可能再改变顶层 HTTP 状态码——连接已经落定为 200。流已经开始后才出现的失败走 SSE 内嵌的 error 对象,见下文流式错误。
重试策略
只建议自动重试临时错误:
429 rate_limit_exceeded:降低并发并按指数退避重试。429 daily_credit_limit_exceeded:重试前先看错误体里的reset_at。500、503:使用指数退避重试。
不要自动重试 400、401、402、404、413。应先修正请求参数、凭证、余额或模型选择。
遇到 context_length_exceeded 时,缩短或摘要历史消息、减少工具定义,或降低 max_completion_tokens——GLM 5 自己不会削这个值,所以传得过大时,会直接拿到所选模型自身限制返回的失败。不同模型的上下文与输出限制可能不同。请求失败后,已经预占的积分会进入退款/结算流程。
async function withBackoff<T>(operation: () => Promise<T>): Promise<T> {
let delay = 500;
for (let attempt = 0; attempt < 4; attempt += 1) {
try {
return await operation();
} catch (error: any) {
const status = error?.status;
if (![429, 500, 503].includes(status) || attempt === 3) {
throw error;
}
await new Promise((resolve) => setTimeout(resolve, delay));
delay *= 2;
}
}
throw new Error('Unreachable');
}
流式错误
上面表格里的同步拒绝(invalid_api_key、insufficient_quota、daily_credit_limit_exceeded 等)都是在流打开之前返回的,是普通的 HTTP 错误响应——无论原始请求是否 stream: true,都能直接用 error?.status 判断,就像上面 withBackoff 那样。
一旦 stream: true 的响应已经开始发送 data: chunk,HTTP 状态码就已经落定为 200,不可能再变。这之后出现的失败会以流内的终止 error 对象形式出现,然后连接关闭:
data: {"error":{"message":"Internal server error.","type":"server_error","code":"internal_error","param":null}}
有两个 code 只会以这种方式出现,永远不会是顶层 HTTP 状态码:
| Code | 含义 | 建议重试? |
|---|---|---|
stream_timeout | 上游流超过 30 分钟仍未结束——请求是纯转发,只有在提供方自己一直不关闭流时才会出现。 | 是 |
client_cancelled | 客户端自己在请求完成前断开或中止了连接。 | 不适用(由客户端导致) |
通过 SSE payload 里的 error.code 字段可以把这两种情况和真正的 internal_error 区分开:一个反复出现 stream_timeout 的请求,需要的是缩小请求,而不是对着一个不健康的后端反复重试。
高峰期 GLM 模型的首字延迟可能达到 20–30 秒,长回答可能持续数分钟。GLM 5 不会在提供方之上再叠加自己的超时:如果提供方 25 秒内没有任何输出,会用同一个模型换一条路由重试一次;否则请求会一直运行到提供方结束。长回答请把 HTTP 客户端的读超时设得宽裕一些(数分钟)——30 或 60 秒的客户端超时会掐掉本来能成功的请求。
排查上下文持续增长导致的失败
如果一个长时间运行的客户端开始出现 402 insufficient_quota,建议按顺序检查:
- 最近一次
usage.prompt_tokens。 - 客户端是否每轮都重新发送完整历史。
- 当前
max_completion_tokens设置。 - 剩余的「可用于 Public API」积分(402 响应体中的
api_eligible_credits,或/settings/apikeys页面的「可用于 Public API」)——当余额里包含试用 / 注册赠送积分时,这个数字会小于总余额;年付订阅和裂变奖励积分在首次付费后可以用于 API。详见计费与限制。
更多方法见上下文与成本控制。