大模型 API 报错排查手册:401 / 429 / 402 分别该改什么
免费 API 最常见的挫败感,不是领不到 Key,而是拿到 Key 之后返回一行看不懂的报错。
更麻烦的是报错码在不同平台的含义并不统一:同样是 429,在 A 家等 10 秒就好,在 B 家等一天也没用;同样是「额度没了」,有的返回 402、有的返回 403、有的直接给你 400。
这篇手册按「下一步该干什么」组织,而不是按状态码罗列。全文提到具体平台的额度与限速时,一律以资源库对应平台页为准(每页都标了核实日期)。
先用 30 秒定位
| 你看到的 | 大概率是什么 | 跳到哪一节 |
|---|---|---|
| 401 / 403 | 鉴权方式或权限问题 | 第二节(改配置) |
| 404 | base_url 或模型 ID 写错 | 第三节(改请求) |
| 400 / 413 / 422 | 参数错误或上下文超限 | 第三节(改请求) |
| 402 / 部分 403 | 额度或余额没了 | 第四节(查账号) |
| 429 | 撞速率限制 | 第五节(该等) |
| 超时 / 流式断在半路 | 网络或服务端 | 第五节(该等) |
一个贯穿全文的前提:报错分两种性质——「你写错了」和「平台给不了」。前者改代码立刻见效,后者再改代码也没用。所以排查的第一步不是读报错,而是先确定这条请求在别的平台能不能跑通:能跑通 → 大概率是你在这家的配置问题;跑不通 → 大概率是请求本身有问题。
一、改配置:401 与 403
401 几乎只有一个意思:服务端不认你的身份。免费平台里它有四个高频成因。
① Key 没生效。 新建的 Key 有时需要几秒到几分钟才生效,粘贴后立刻调用会失败。等一分钟再试,「代码没动却好了」基本都是这个原因。
② 鉴权头名字不是 Authorization。 这是最容易被忽略的一类——OpenAI 兼容 ≠ 只认 Bearer。有平台把认证头定义成 api-key,此时用标准写法必然 401,而报错信息不会告诉你该换成什么。用 OpenAI SDK 时得显式指定:
from openai import OpenAI
client = OpenAI(
api_key="你的Key",
base_url="https://example.com/v1",
default_headers={"api-key": "你的Key"}, # 少数平台的认证头不是 Authorization
)
遇到「Key 明明是对的却 401」,第一件事是去平台页翻认证方式那一栏,而不是反复重新生成 Key。
③ 复制时带了空格或换行。 从网页复制 Key 常把换行一起带上,api_key.strip() 一下能解决相当一部分「玄学 401」。
④ 环境变量没刷新。 用 setx 或写进 ~/.zshrc 之后,当前已开的终端不会自动生效,新开一个窗口再试。
403 比 401 更「业务」一些:常见于 Key 权限不足(比如只授权了部分模型)、账号未完成实名或未开通对应模型服务、以及请求来源被限制。403 先读响应体,它通常比 401 说得更具体。
二、改请求:404 / 400 / 413
404:八成是 base_url 的锅
模型 ID 写错一般返回 404 或 400,但绝大多数 404 是 base_url 写错了。三个高发点:
- 少了
/v1,或反过来多写了/v1(有的平台 base 里已经含版本号)。 - 路径拼重了:SDK 会自动在 base 后面拼
/chat/completions,如果你把完整接口地址粘进base_url,就会变成.../chat/completions/chat/completions。 - 接口换版本了:平台升级 API 后旧地址会失效。有平台明确下线过 V1 接口,旧
base_url调用直接失败、必须迁到新版地址——这类失效的特点是”以前能跑、现在不能跑”,且报错与你的代码无关。
模型 ID 的高发点则是大小写与斜杠:同一个模型在 A 家叫 glm-4-flash,在 B 家可能叫 zhipu/glm-4-flash;开源系模型常带组织前缀(形如 Qwen/Qwen3-xxx)。别凭记忆写模型名,从平台页复制。
400 / 413 / 422:参数与上下文
这一类的定位方法是「看服务端回给你的字段名」,它通常会明确指出哪个参数不合法。
| 症状 | 真实原因 | 处理 |
|---|---|---|
| 413 或「too many tokens」 | 输入超过模型上下文 | 裁历史消息或换长上下文模型 |
400 提到 max_tokens | 输出上限设太大 | 调小,见下方说明 |
| 400 提到 messages 格式 | 角色名或结构不合规 | 检查是否误用了系统级字段 |
| 400/402 提到额度 | 其实不是参数问题 | 转到第四节 |
一个很容易踩的坑:上下文长度 ≠ 单次输出上限。 有的模型上下文 256K,但单次输出被限制在 64K 以内;你按上下文长度去设 max_tokens,就会稳定吃 400。另有平台的上下文是 1M、输出 128K。这两个数字要分开查。
还有几个平台特有的口径差异值得记住:
- 有的平台按「Prompt 次数」计费而不是 Token:单次对话算 1 次,长上下文不会多扣,但 Agent 多轮和批量调用消耗飞快。
- 超限返回的码不一定是 429:有的平台免费令牌用满后返回的是 400,如果你只按 429 做重试逻辑,就会一直重试一个永远不会成功的请求。
流式输出相关的 400
开 stream=True 之后报 400,常见于:SDK 版本过旧不认流式参数、把非流式响应当流解析、以及代理层不支持 text/event-stream。先用非流式跑通,再开流式——这样能把「请求本身错」和「流式链路错」分开。
三、查账号:额度为什么「突然没了」
这类问题的迷惑性在于:代码一个字没改,昨天还好好的,今天全红。免费额度消失基本逃不出下面五种。
① 额度是「按模型独立」的。 你在 A 模型上还有额度,不等于 B 模型能用。有平台明确写了免费额度按模型独立发放、不能跨模型合并使用。切了个新模型就报错,先看额度是不是跟着模型走的。
② 额度带有效期,且各平台差异巨大。 有 30 天的、90 天的、2 周的、还有 5 小时窗口滚动恢复的。“送了多少”远不如”什么时候到期”重要——建议给每个平台记一个到期日(活动快讯里的截止日期可以直接拿来用)。
③ 重置时间不是你的「今天」。 有平台按 UTC 零点重置,与北京时间差 8 小时;有平台在北京时间凌晨 2 点重置;还有按 5 小时/7 天窗口滚动的。你以为是”今天没额度了”,其实只是跨过了它的重置边界。遇到「睡一觉就好了」的报错,基本属于这一类。
④ 额度用尽后服务被自动暂停,而不是降级。 这一点必须区分:
| 行为 | 后果 | 应对 |
|---|---|---|
| 降级 | 请求变慢或换小模型,仍能用 | 可接受 |
| 直接报错 | 请求失败 | 必须做容错 |
| 服务暂停 | 需要手动去控制台开启 | 必须监控 + 提醒 |
有平台的免费额度用尽且未关闭「安心模式」时,对应模型服务会自动暂停——不是报错,是彻底不响应,直到你手动处理。这类平台绝不能挂主力业务。
⑤ 网页版和 API 是两套体系。 这是新手最常误判的一条:某平台网页版聊天免费,但API 额度完全独立,Key 不通用、额度不共享。看到「网页能聊」就以为 API 也能用,必然踩坑。
判断额度问题的通用顺序:① 换一个模型名试试(排除按模型发放)→ ② 去控制台看真实剩余量而不是看报错 → ③ 核对重置时区 → ④ 确认是否需要手动开启服务 → ⑤ 最后才怀疑 Key 失效。
四、该等:429 与超时
429 是免费用户的老朋友。但它至少对应三种完全不同的限制,处理方式完全不同:
| 类型 | 触发 | 恢复方式 |
|---|---|---|
| RPM / TPM 速率限制 | 请求太密集 / 单请求太大 | 秒级到分钟级,退避即可 |
| 日请求上限(RPD) | 一天调用次数用满 | 等重置(注意时区) |
| 临时限流 | 高峰负载 | 等几分钟,或换平台 |
判断技巧:看响应头。 不少平台会返回 Retry-After 或 x-ratelimit-* 系列头,直接告诉你该等多久。有 Retry-After 就听它的,别自己拍脑袋。
判断”该等还是该换”:如果错误在几秒内重复出现且带 Retry-After,是速率限制,退避有效;如果退了 3 次还在报,多半是当天配额用尽,继续重试只是浪费你的时间——该切备用平台了。
速率限制的量级差异也同样巨大:宽松的按千次/天计,严格的只有个位数 RPM。同一个脚本在 A 家跑得好、在 B 家一跑就炸,八成是把自己写成了并发压测。
可运行的退避重试
把「无脑重试」换成「按错误类型分流」,是这段代码唯一的价值所在:
import random
import time
from openai import OpenAI, APIStatusError, APIConnectionError, APITimeoutError
client = OpenAI(api_key="你的Key", base_url="https://example.com/v1")
# 可以重试的:限流、服务端错误、网络问题
RETRYABLE = {429, 500, 502, 503, 504}
# 不该重试的:改了也没用,重试只是浪费配额
FATAL = {400, 401, 403, 404, 413, 422}
def ask(prompt, model, max_retries=4):
for attempt in range(max_retries):
try:
resp = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
)
return resp.choices[0].message.content
except APIStatusError as e:
if e.status_code in FATAL:
raise # 参数/鉴权错,重试无意义
if e.status_code not in RETRYABLE:
raise
# 服务端给了 Retry-After 就听它的
wait = None
if e.response is not None:
ra = e.response.headers.get("retry-after")
if ra and ra.isdigit():
wait = float(ra)
# 否则指数退避 + 抖动(避免多进程同时重试、再次撞在一起)
if wait is None:
wait = min(2 ** attempt, 30) + random.uniform(0, 1)
if attempt == max_retries - 1:
raise # 交给上层做切换
time.sleep(wait)
except (APIConnectionError, APITimeoutError):
if attempt == max_retries - 1:
raise
time.sleep(min(2 ** attempt, 30) + random.uniform(0, 1))
print(ask("用一句话介绍你自己", "某免费模型"))
两个细节值得单独说:
- 抖动(jitter)不是可选项。 指数退避如果不加随机量,多个进程会同时醒来、同时重试,把限流打成持续限流。
FATAL列表是这段代码的核心。 区分「重试有用」和「重试有害」,比退避算法本身重要得多——反复重试一个 401,只会让你在平台的监控里更难看。
超时与流式中断
超时几乎永远是三个原因:请求太大、网络到该平台不稳、以及平台本身在高峰。处理顺序是 先给客户端显式设 timeout(默认值通常过长或过短)→ 再降 max_tokens → 最后换平台。
流式中断要分两种看:断在开头是连接没建起来,重试即可;断在中途则已经消耗了 Token 却拿不到完整结果,这时应该把已收到的内容存下来、缩小输入重试,而不是原样重来。
五、三个常见误判
误判一:把「平台限流」当成「我的代码有 bug」。 特征是时间不规律、报错时有时无、换个时间点就好了。
误判二:把「免费额度取消」当成「Key 失效」。 平台调整政策是很常见的运营动作,有平台明确取消过原先的免卡额度、也有关闭过免费档。看到全网都在报同一个错,先去平台页和活动快讯看一眼口径变化。
误判三:认为「报错信息里写的就是真相」。 前面说过,额度耗尽可能伪装成 400。永远以控制台的真实数据为准。
六、排查完之后:让它自动处理
手动排查只能解决问题一次,代码里的分流逻辑才能解决问题一辈子。这正好是下一篇的内容:
- 想让一家挂了自动切下一家?看 《多模型 Fallback 实战》
- 429 反复出现、想做批量任务?看 《免 429 指南:限流、并发与批量任务》
- 想把免费额度接到 AI 编程工具里?看 《用免费 API 白嫖 AI 编程》
- 担心用到了来路不明的渠道?先读 避坑指南
- 具体某个平台的限速与额度口径,一律查资源库平台页
本文只讲方法与口径,不保证任何具体额度长期有效——文中提到的平台请以 资源库平台页 为准,每页都标注了核实日期。
还没开始?从 《领取第一个免费 Key》 读起。担心踩坑,先看 避坑指南。