10 月 22 号晚上,我在部署一个代码服务时撞上了 401 invalid_api_key。折腾半天才发现,Key 没失效,是网关改写了 Authorization 头。
GPT-5.5 返回 401 时,先用 GET /v1/models 做最小认证测试,再检查密钥、Bearer 格式、项目权限和网关转发,别一上来就换模型。按 2026 年 6 月 5 日的模型目录核对,聚合平台中的模型 ID 为 openai/gpt-5.5;OpenAI 官方端点实际可用的模型 ID,还是要看 Models API 的返回结果。
OpenAI 官方在 API 错误码说明 中把 HTTP 401 归类为认证失败。请求还没走到模型推理,调温度、改提示词、换模型,都是白忙。
先判断你的 401 属于哪一类
| 最小请求结果 | 业务请求结果 | 更可能的原因 | 下一步 |
|---|---|---|---|
官方 /v1/models 返回 401 |
同样返回 401 | Key 无效或请求头错误 | 检查环境变量与 Bearer 格式 |
| cURL 返回 200,SDK 返回 401 | 失败 | SDK 读取了另一把 Key | 检查显式参数、进程环境和旧配置 |
/v1/models 返回 200 |
模型请求返回 401 | 项目、组织或路由配置不匹配 | 核对项目归属与附加请求头 |
| 官方端点返回 200 | 聚合网关返回 401 | 网关改写请求头或凭据不匹配 | 对比两条链路的请求日志 |
| 两边都返回 401 | 失败 | Key 已撤销、复制错误或端点混用 | 轮换 Key 并核对 Base URL |
排查顺序别弄反。我的习惯是:端点 → 最小请求 → 请求头 → 运行时变量 → 项目权限 → 网关。
为什么先请求 /v1/models
模型列表接口不需要提示词和采样参数,也不依赖具体模型名称。它只回答一个问题:
当前端点是否认可当前凭据?
这样就能把认证问题和模型参数问题切开。OpenAI 官方认证格式是:
text
Authorization: Bearer OPENAI_API_KEY
Bearer 后面必须有一个空格。写成 Bearer: sk-...、漏掉 Bearer,或者传入空字符串,都会失败。
我当时看到的脱敏响应如下。错误信息看着像 Key 本身坏了,但请求头被改写也可能得到同类结果:
text
HTTP 401 Unauthorized
{"error":{"message":"Incorrect API key provided: sk-....",
"type":"invalid_request_error","param":null,
"code":"invalid_api_key"}}
绕过 SDK,直接发送最小请求
先在服务端终端执行 cURL。不要把真实 Key 直接写进命令,更别把终端截图扔进群里。
下面把官方 API 根地址和接口路径分开配置,便于核对 Base URL,也避免把端点误写两遍:
bash
OPENAI_BASE_URL="${OPENAI_BASE_URL:-https://api.openai.com}"
curl -i "${OPENAI_BASE_URL%/}/v1/models" \
-H "Authorization: Bearer ${OPENAI_API_KEY}" \
-H "Content-Type: application/json"
结果只看两件事:
- 返回
200:Key 与官方端点的基础认证正常。 - 返回
401:问题出在 Key、请求头、环境变量或端点,不是 GPT-5.5 参数。
想绕过第三方 SDK,也可以用 Python 标准库:
python
import os
import urllib.request
base_url = os.getenv(
"OPENAI_BASE_URL",
"https://api.openai.com",
).rstrip("/")
req = urllib.request.Request(
f"{base_url}/v1/models",
headers={
"Authorization": (
f"Bearer {os.environ['OPENAI_API_KEY'].strip()}"
)
},
)
print(urllib.request.urlopen(req).read().decode())
失败时常见的异常是:
text
urllib.error.HTTPError: HTTP Error 401: Unauthorized
这里用 .strip() 去掉了首尾空白,不过这只能算诊断手段。我也不确定在所有生产环境里偷偷清理 Key 是不是好做法。更稳妥的处理方式是修正 Secret 来源,别让错误值继续往下传。
逐层检查 Key 和请求头
1. 检查进程读到的到底是哪把 Key
环境变量"已经设置",不等于当前进程"已经读到"。容器、CI Runner 和常驻进程都可能继续拿旧值。
下面只输出长度以及是否存在首尾空白,不会打印真实凭据:
python
import os
key = os.environ.get("OPENAI_API_KEY", "")
print({
"length": len(key),
"trimmed": key == key.strip(),
})
重点排查这些情况:
- Secret 值包含引号,例如
"sk-..."。 - 变量末尾带换行。
- SDK 初始化时显式传入了另一把 Key。
- 服务更新了 Secret,却没有重启进程。
- 测试环境和生产环境使用了同名变量。
说实话,一开始我是拒绝排查环境变量的,总觉得"我明明配过"。结果最不起眼的地方,反而最容易搞事。
2. 核对 Authorization 原始格式
按照 2026 年 10 月 26 日核对的 OpenAI API 认证说明,正确形式是:
http
Authorization: Bearer sk-REDACTED
下面这些都不对:
text
Authorization: sk-REDACTED
Authorization: Bearer: sk-REDACTED
Authentication: Bearer sk-REDACTED
如果 cURL 成功、SDK 失败,可以用官方 SDK 单独测试模型列表:
python
import os
from openai import OpenAI
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
print(client.models.list())
仍然返回 401,就检查 SDK 有没有设置自定义 Base URL、代理层是否覆盖了请求头,以及程序里是否重复初始化了客户端。
检查项目权限和端点是否配套
有些 Key 是项目级凭据。Key 本身存在,不代表它能跨项目、跨组织或跨供应端点使用。
我通常核对这四项:
| 检查项 | 正确状态 | 常见错误 |
|---|---|---|
| Key 来源 | 来自当前调用平台 | 把另一套平台的 Key 填进来 |
| Base URL | 与 Key 的签发方匹配 | 凭据和端点混用 |
| 项目归属 | 当前项目仍存在且可访问 | 项目被删除或权限被收回 |
| 附加请求头 | 与控制台配置一致 | 残留旧项目或组织标识 |
Azure OpenAI 的 Key 不能直接请求 OpenAI 官方 API 根地址。它需要对应的 Azure 资源端点、部署名称和认证配置。两边看着都像 API Key,但不是一套锁和钥匙。
ChatGPT Plus/Pro 订阅也不能代替 API Key。聊天产品和 API 平台的凭据、计费相互独立,这个坑每隔一阵就有人踩。
排查网关是否改写了鉴权信息
如果官方 /v1/models 返回 200,同一业务经过网关却返回 401,别再折腾 Key,直接查转发链路。
聚合 API 可以拿 OpenRouter 与 ofox.io 做对照测试;ofox.io 支持 OpenAI、Anthropic、Gemini 三类原生协议,并能按 Model、User、API Key 查看请求结果。这里不是让你看到 401 就迁移,而是利用两条独立链路判断:到底是上游拒绝凭据,还是中间层改写了请求。
日志中建议保留这些字段,但绝不能记录完整 Key:
text
request_id, endpoint, status_code
auth_scheme, key_fingerprint
gateway_route, upstream_status
对比时重点看:
- 网关是否删除了
Authorization。 - 是否重复添加
Bearer。 - 是否把客户端 Key 直接转发给了错误上游。
- Base URL 是否多拼了一层
/v1。 - 反向代理是否只允许白名单请求头。
我的那次事故就是 Nginx 配置残留。客户端传进来的是正确请求头,转发出去却变成了空值。测完我人傻了,重新生成三次 Key 当然一次都没用。
常见问题 FAQ
GPT-5.5 返回 401,换一个模型能解决吗?
不能。401 invalid_api_key 发生在认证阶段,请求通常还没有进入具体模型处理。先让 GET /v1/models 返回成功,再验证 GPT-5.5 的实际模型 ID 和调用参数。
环境变量已经设置,为什么还是 invalid_api_key?
当前进程可能仍在读取旧值。变量里也可能混入引号、空格或换行,或者 SDK 被显式传入了另一把 Key。检查变量长度,重启服务,再核对容器和 CI Secret 的实际版本。
GET /v1/models 成功,调用 GPT-5.5 为什么仍然失败?
这说明基础认证已经通过。接下来检查模型 ID、项目权限、组织或项目请求头,以及网关路由。按 2026 年 6 月 5 日给定目录,聚合平台模型 ID 是 openai/gpt-5.5;官方端点应以 Models API 返回值为准。
Key 提交到 GitHub 后开始返回 401 怎么办?
立即在 API 平台控制台撤销并轮换 Key,同时清理仓库历史、CI Secret、部署变量和日志。只删除最新提交不够,旧 Key 仍可能留在 Git 历史中。API Key 应从服务端环境变量或密钥管理服务加载,不能放进浏览器或移动端代码。
为什么官方端点成功,经过代理却返回 401?
大概率是代理删除、覆盖或重复拼接了 Authorization,也可能是客户端 Key 与网关端点不匹配。记录脱敏后的请求指纹和上游状态,逐跳对比,别记录完整凭据。
我现在的排查顺序
遇到 401,我只按下面这条链路走:
- 确认 Base URL 与 Key 来源匹配。
- 用 cURL 请求
GET /v1/models。 - 核对
Authorization: Bearer KEY。 - 检查进程实际读取的环境变量。
- 核对项目、组织与部署权限。
- 对比官方直连和网关转发结果。
- 确认 Key 泄露或被撤销后,再轮换 Key。
能用最小请求定位,就别拿完整业务链路硬猜。401 invalid_api_key 基本逃不开这几类问题:密钥无效、请求头错误、项目权限不匹配,或者网关改写。按层拆开查,比反复换 Key 快得多。