排查 401 的一个常见误区,是把它直接等同于 API Key 失效。HTTP 401 的真正含义是:当前请求没有携带服务端认可的认证信息。服务端是否认为该 Key 已过期或被撤销,通常要由响应体中的错误码、提示信息与账号状态来进一步判断;客户端能更快定位的,是 Key 没有按正确格式出现在请求里。这个区别决定了排查顺序:先反查实际发出的请求,再考虑重置密钥。
对 DeepSeek API 接入过程中的 401,下面以“从 request 反查 config”为主线整理一条可操作的排查流程。本文只写通用 HTTP 客户端的认证排查步骤;涉及 DeepSeek 官方鉴权字段、有效期和具体错误码的内容,以你接入时最新的官方 API 文档为准。示例中的环境变量名、Header 名称和 URL 都应按项目实际配置替换。
先把实际发送的 Authorization 打出来
在 Python 的 requests 库中,收到 401 后依然可以通过 resp.request.headers 查看这次请求实际组装的请求头。这个动作的价值在于:它检查的不是“我构造请求时传入的变量”,而是“请求对象最终携带出去的 Header”。如果认证信息在某个 Session、HTTP 封装层或代理适配器内部被改写,单看构造请求的代码是发现不了的。
一个安全的最小检查脚本如下:
import os
import requests
def mask_secret(value: str) -> str:
if not value:
return ''
if len(value) '
print('status:', resp.status_code)
print('Authorization 结构:', display)
print('响应摘录:', resp.text[:200])
注意:不要把 resp.request.headers.get('Authorization') 直接写进日志。打印完整请求头会把 Key 泄露到日志系统。示例里的 mask_secret 只保留前两个与后两个字符,避免意外泄密。
实际运行时可能出现两种结果:一是打印出来的 Authorization 为空,说明请求根本没有带上认证信息;二是打印出来的结构与预期不符,比如少了 Bearer 前缀、多了引号、Key 中包含不可见字符等。这两种情况都指向请求组装层,而不是服务端撤销了 Key。
值得逐项核对的配置点
1. Key 来源是否干净
环境变量是 API Key 最常见的载体,也最容易出现“代码以为读了,实际没读到”的问题。先用最直接的方式确认:
raw_key = os.environ.get('DEEPSEEK_API_KEY', '')
print('长度:', len(raw_key))
print('是否存在首尾空白:', raw_key != raw_key.strip())
如果长度是 0,说明应用进程没有继承这个环境变量。很多部署平台只在进程启动时注入一次环境变量,修改 .env 或控制台 Secret 后必须重启应用进程才能生效。
如果存在首尾空白,优先修正配置来源,而不是在代码里做 strip。原因是某些 Key 可能是从网页复制时多带了一个换行或空格,也可能是配置平台在值外面额外包了一层引号;在代码里盲目 strip 可能掩盖配置本身的错误,导致下次部署时再次出现 401。
2. Authorization Header 是否按文档组装
大部分使用 Bearer 方案的 HTTP API 要求 Header 格式写成:
Authorization: Bearer
这里容易出错的位置有三个:
- Header 名称不是
Authorization,而是某个自定义 Header; - 值中只有 Key,没有 Bearer scheme;
- Bearer 与 Key 之间缺少空格,写成
Bearer。
在修改格式之前,应回到官方 API 文档确认当前接口要求的 Header 名与认证方案。如果服务端在 401 响应中返回了 WWW-Authenticate 响应头,这个 Header 通常也会说明服务端期望的认证方式,可以作为现场证据一并收集。
3. 是否存在中间层改写
如果检查后发现本地代码生成的 Header 正确,但请求仍然 401,就要考虑中间层是否改写了认证信息。常见的改写位置包括:
- HTTP 客户端统一封装的默认 Header;
- 自定义拦截器或请求钩子;
- 代理、API 网关、Sidecar;
- 统一鉴权组件在转发请求时用网关自己的身份替换了原始 Key。
这类问题只靠阅读业务代码未必能发现。更有效的做法是在最外层记录最终发出的 Header。Python 的 resp.request.headers 提供了一个观察点;如果使用了其他语言或框架,也要找到类似“Prepared Request”或“Request Message”层面的检查入口。
4. Key 是否已经离开有效凭据集合
在完成上述检查后,如果实际发出的 Header 与官方文档完全一致,但服务端仍然返回 401,才需要把问题收敛到 Key 本身。此时可以生成一个新 Key,并在最小请求中直接替换测试。如果新 Key 能通过,说明旧 Key 已被轮换、删除或禁用,需要从配置源头同步更新。
排查时还要注意一点:不要让同一个 Key 在多个配置文件中重复出现。如果代码、本地 .env、部署平台 Secret 中各有一份拷贝,就会出现“控制台里已经重置,但实际请求仍使用旧 Key”的情况。
用最小请求区分 SDK 与服务端问题
当 401 出现在业务代码中时,先用 curl 发一个不带 SDK 的最小请求,可以快速区分问题属于“服务端不认这个 Key”还是“SDK 组装请求时引入了额外变量”。
curl -i \
-H "Authorization: Bearer ${DEEPSEEK_API_KEY}" \
https://api.example.com/v1/your-endpoint
这里建议使用 -i 查看响应状态码与响应头,而不是直接使用 -v。-v 会把整个请求过程打到 stderr,其中包含完整的 Authorization Header;如果随后把终端内容粘贴到工单或社区,Key 就会泄露。
curl 请求成功后,再回到业务 SDK 中检查 base_url、API Key 参数名和 Header 注入方式。curl 失败时,则应优先怀疑 Key 本身或请求地址是否正确。
一份可直接照做的排查顺序
对于 DeepSeek API 返回 401 的场景,最终可以收敛为如下顺序:
- 确认
DEEPSEEK_API_KEY环境变量已注入当前进程,且值不为空。 - 检查 Key 是否存在首尾空白、多余引号或换行。
- 对照最新官方 API 文档,确认 Header 名称与认证方案。
- 打印请求实际发出的 Authorization 结构,确认 Bearer scheme 与 Key 之间的分隔正确。
- 检查 SDK 或 HTTP 客户端的全局配置,是否在请求发出前覆盖了认证 Header。
- 使用 curl 最小请求绕过 SDK,确认 401 是否仍然存在。
- 在官方控制台生成新 Key,用新 Key 复测,排除旧 Key 已被撤销或轮换的情况。
- 保留响应体中的错误码和 Request ID,用于后续对照官方错误码说明。
最后补充一点:如果官方文档明确给出了 401 对应的错误码解释,应以官方说明为准。HTTP 401 只能告诉开发者认证没有通过,不能代替服务端回答“Key 为什么不被接受”。把排查重点放在实际发送的请求上,是定位这类问题成本最低的路径。