1. 401 报错到底卡在哪一环先分清是没带钥匙还是钥匙不对Codex 报401 unauthorized这件事我前后帮人排查过不下二十次发现一个规律绝大多数人一看到 401 就本能地去重新登录、重新生成密钥折腾半天还是报错最后发现根本不是密钥的问题。所以这篇我打算把整个排查链路拆开讲从报错文本的细微差别入手一步步定位到真正的原因而不是让你盲目试错。先说清楚 Codex 在这里指什么。它是 OpenAI 推出的一套命令行编程助手工具Codex CLI可以跑在终端里也可以作为插件集成到 VS Code 这类编辑器里。它的工作方式是你在本地敲命令它把请求发到远端模型接口拿到结果再返回给你。而401 unauthorized这个状态码在 HTTP 语义里非常明确——服务器认为你没有通过身份认证。注意是认证authentication失败不是授权authorization不足后者通常是 403。这个区别很关键它把问题范围直接缩小到了身份凭证这一层。但身份凭证这四个字背后其实有好几种可能凭证压根没传、传了但格式不对、传了但内容无效、传了但发给了错误的地址、或者凭证本身权限不够。这五种情况在 Codex 里都会表现为 401但报错文本的细节完全不同。我整理了一张对照表你可以先对着自己的报错信息定位报错文本关键片段真实含义大概率原因missing bearer or basic authentication请求里根本没带认证头环境变量没设置或配置文件没被读取api_key_required服务端要求提供密钥但没收到密钥变量名为空或拼写错误invalid_api_key密钥格式对但服务端不认密钥失效、复制错误、或用了别家的密钥incorrect api key provided: sk-xxx****密钥内容错误密钥被截断、含多余空格、或已撤销insufficient permissions密钥有效但权限不足账号套餐、组织权限或模型访问权限问题auth token is unavailable本地缓存的登录态丢失登录会话过期需要重新登录这张表是我踩坑踩出来的经验总结不是官方文档抄的。你只要把报错原文往里一套基本就能锁定方向。下面几节我会按从最常见到最隐蔽的顺序把每一类问题的排查和修复讲透。提示排查前先把完整报错原文复制下来别只看最后一行。Codex 的报错经常是多层嵌套的最外层是unexpected status 401里面还包着一层 JSON真正的线索往往在内层。2. 环境变量这条线OPENAI_API_KEY 为什么设了还是没用2.1 变量名拼错和大小写问题比你想的更常见我遇到最多的一个情况就是用户信誓旦旦说我明明设了环境变量结果一查变量名写成了OPEN_AI_API_KEY或者OPENAI_APIKEY。Codex 读取的是OPENAI_API_KEY一个下划线都不能差大小写也必须完全一致。在 Linux 和 macOS 上环境变量是区分大小写的openai_api_key和OPENAI_API_KEY是两个完全不同的变量。验证方法很简单在终端里敲# Linux / macOS echo $OPENAI_API_KEY # Windows PowerShell echo $env:OPENAI_API_KEY # Windows CMD echo %OPENAI_API_KEY%如果输出是空的那问题就找到了。如果输出了一串sk-开头的字符说明变量设对了继续往下查。2.2 设了变量但当前终端读不到会话隔离的坑这是第二个高频坑。很多人在.bashrc或.zshrc里加了export OPENAI_API_KEYsk-xxx然后直接在已经打开的终端里跑 Codex结果还是 401。原因是修改配置文件不会影响已经打开的终端会话。你必须新开一个终端窗口或者手动执行source ~/.zshrc让配置生效。Windows 上更麻烦一点。如果你是用图形界面系统属性 → 环境变量设置的那么已经打开的 CMD 或 PowerShell 窗口同样读不到新值必须关掉重开。而且 Windows 分用户变量和系统变量如果你在用户变量里设了但 Codex 是以管理员身份运行的那它读的是系统变量两者不互通。我个人的习惯是排查阶段直接在启动 Codex 的同一个终端里临时 export 一次确认能通之后再写进配置文件export OPENAI_API_KEYsk-你的密钥 codex这样能快速区分是变量没生效还是是密钥本身有问题。2.3 密钥里的隐形字符复制粘贴的陷阱从网页上复制密钥的时候很容易带上首尾的空格、换行符甚至是一些不可见的 Unicode 字符。这些字符在终端里看不出来但会让服务端认为密钥无效返回incorrect api key provided。判断方法把密钥用引号包起来 echo 一下看长度对不对。正常的密钥长度是固定的如果你 echo 出来的字符数比预期多那多半混进了杂质。更稳妥的做法是用cat -A查看隐藏字符echo -n $OPENAI_API_KEY | cat -A如果行尾出现了^M或者$之外的东西就说明有问题。修复方式就是重新复制或者手动把密钥写进配置文件而不是靠粘贴。注意密钥一旦泄露比如贴到了公开的聊天记录、截图、代码仓库里要立刻去后台撤销并重新生成。401 有时候反而是好事说明泄露的密钥已经被系统判定失效了。3. codex login 与 API Key 两套认证机制别混着用3.1 登录态认证和密钥认证是两条独立的路Codex 支持两种认证方式一种是通过codex login走账号登录凭证会缓存在本地另一种是直接配置 API Key。这两套机制是互相独立的但很多人会把它们搞混导致认证冲突。如果你用的是codex login登录那么凭证存在本地的配置目录里通常是用户主目录下的隐藏文件夹。这种情况下即使你没有设置OPENAI_API_KEY也应该能正常使用。反过来如果你设置了 API Key但本地还残留着过期的登录态有时候反而会互相干扰。排查登录态问题可以这样操作# 查看当前登录状态 codex auth status # 如果显示未登录或已过期重新登录 codex login # 退出登录清掉本地缓存 codex logout我遇到过一种情况用户之前登录过后来账号换了但本地缓存没清结果一直报auth token is unavailable。解决办法就是先logout再login把旧凭证彻底清掉。3.2 什么时候该用登录什么时候该用密钥这里给个我自己的判断标准。如果你只是个人使用、图省事用codex login最方便它会自动处理凭证刷新。但如果你需要在 CI/CD 流水线里跑、或者要在多台机器上统一配置、又或者要接入第三方兼容接口那就必须用 API Key因为登录态没法在无头环境里维持。还有一个细节有些第三方兼容服务比如把 Codex 指向别的模型接口只认 API Key不认登录态。这种情况下你就算登录了也没用必须老老实实配密钥。3.3 登录后仍报 401 的排查顺序如果你确认已经登录但还是 401按这个顺序查先codex auth status看登录态是否有效检查是否有残留的OPENAI_API_KEY环境变量在抢戏如果有先 unset 掉检查配置文件里是否同时存在登录凭证和密钥配置两者冲突时以哪个为准要看具体版本确认账号本身没有被限制或欠费# 临时清掉环境变量测试纯登录态能否工作 unset OPENAI_API_KEY codex这一步能帮你快速判断问题出在登录态还是密钥上。4. 接入第三方兼容接口base_url 配错是最隐蔽的 4014.1 为什么改了 base_url 反而报 401现在很多人会把 Codex 指向第三方兼容接口来用比如把codex_base_url设成某个兼容 OpenAI 协议的服务地址。这时候 401 的成因就多了一层你用的密钥是 A 家的但请求发给了 B 家。B 家当然不认 A 家的密钥直接返回 401。热词里出现的set codex_base_urlhttps://api.deepseek.com/v1和set openai_api_keysk-xxx就是典型场景。如果你把 base_url 指向了 DeepSeek 的接口那OPENAI_API_KEY里就必须填 DeepSeek 的密钥而不是 OpenAI 的密钥。这两者不通用。排查方法确认你的 base_url 和密钥是同一家的。可以先用 curl 单独测一下curl https://api.deepseek.com/v1/models \ -H Authorization: Bearer sk-你的密钥如果这个 curl 返回 401说明密钥和地址不匹配或者密钥本身无效。如果 curl 能通但 Codex 报 401那问题就在 Codex 的配置读取上。4.2 base_url 的路径细节结尾的 /v1 不能少也不能多兼容接口的 base_url 对路径很敏感。有的服务要求结尾带/v1有的要求不带有的甚至要求带完整的/v1/chat/completions。配错了路径请求会打到错误的端点返回的可能是 404也可能是 401取决于服务端的处理逻辑。我建议的做法是先查清楚目标服务的文档确认它要求的 base_url 格式然后严格照抄。不要凭感觉加或减/v1。配置完之后用codex发一个最简单的请求测试看报错是 401 还是 404能帮你区分是认证问题还是路径问题。4.3 代理转发场景下的认证头丢失热词里有个cc switch local proxy failed while handling codex endpoint /responses这说的是通过本地代理转发请求的场景。这种架构下401 的一个常见原因是代理在转发时把认证头弄丢了。请求链路是Codex → 本地代理 → 目标服务。如果代理没有正确透传Authorization头目标服务收到的就是无认证请求返回missing bearer or basic authentication。排查这种问题要在代理层加日志确认转发出去的请求里到底有没有认证头。提示如果你用的是本地代理方案先在代理配置里打开请求日志把转发前后的 header 都打出来对比。这一步能省掉大量猜测时间。5. 从报错文本反推根因一套可复用的排查流程5.1 第一步永远是拿到完整报错很多人排查效率低就是因为只看了一眼401就开始瞎试。正确的第一步是把完整报错复制出来逐字读。Codex 的报错通常长这样unexpected status 401 unauthorized: {code:invalid_api_key,message:Incorrect API key provided: sk-xxx****}这里面invalid_api_key和Incorrect API key provided就是金线索。前者告诉你密钥无效后者告诉你密钥内容错了。对照第 1 节的表格直接定位到密钥内容错误这一类。5.2 第二步用最小化测试隔离变量定位到大致方向后别急着改 Codex 的配置先用 curl 做最小化测试。curl 排除了 Codex 本身的所有干扰能直接告诉你密钥 地址这个组合到底通不通。# 测试密钥和地址是否匹配 curl -s -o /dev/null -w %{http_code} \ https://api.openai.com/v1/models \ -H Authorization: Bearer $OPENAI_API_KEY返回 200 说明密钥和地址没问题问题在 Codex 配置返回 401 说明密钥或地址有问题继续查这两个。5.3 第三步逐层排除配置来源Codex 读取配置的来源可能有多个环境变量、配置文件、命令行参数。这三者的优先级在不同版本里可能不一样。排查时要把所有来源都列出来确认最终生效的是哪一个。# 查看所有可能相关的环境变量 env | grep -i -E openai|codex|api_key # 查看配置文件位置具体路径以你的版本为准 ls -la ~/.codex/ 2/dev/null ls -la ~/.config/codex/ 2/dev/null把环境变量和配置文件里的值都拿出来对比看有没有冲突。我遇到过环境变量里是旧密钥、配置文件里是新密钥的情况结果 Codex 读了环境变量一直报 401。5.4 第四步确认账号和权限状态如果密钥格式、地址、配置来源都排查过了还是 401那就要怀疑账号本身了。可能的情况包括账号欠费、密钥被撤销、组织权限变更、或者访问的模型不在你的套餐范围内。热词里的you have insufficient permissions和the gpt-5.6-sol model is not supported都属于这一类。这时候要去服务商的后台确认账号状态和密钥状态而不是在本地继续折腾。6. 几个容易被忽略的细节和我的实操心得6.1 版本不匹配导致的认证协议变化Codex 更新比较频繁不同版本对认证的处理方式可能有变化。我遇到过升级之后旧配置失效的情况。如果你是在升级后突然开始报 401第一件事就是去看更新日志确认认证相关的配置有没有变更。有时候重新跑一次codex login或者重新生成配置文件就能解决。6.2 多环境共存时的配置污染如果你同时在用多个 AI 编程工具它们可能都读OPENAI_API_KEY这个变量。这时候一个工具的配置可能会影响另一个。我的做法是给每个工具用独立的配置文件而不是全靠全局环境变量。这样能避免改了 A 结果 B 坏了的情况。6.3 网络环境对认证的影响有些网络环境下请求会被中间设备拦截或改写导致认证头丢失或损坏。如果你在某个特定网络下必现 401换个网络就正常那基本可以确定是网络链路的问题。这种情况下检查本地的网络配置和代理设置确认请求是直连还是经过了中间层。6.4 我的排查口诀最后分享一个我自己总结的排查口诀按这个顺序走九成以上的 401 都能定位看报错完整读一遍对照表格定位类别查变量确认变量名、值、生效范围都对测连通用 curl 隔离 Codex测密钥和地址对来源环境变量、配置文件、命令行参数逐个核对验账号确认账号和密钥在服务端的状态正常换环境排除网络和版本因素这套流程的核心思路是从外到内、从简到繁先用最简单的手段排除掉大部分可能再逐步深入到复杂场景。盲目重装、盲目重新生成密钥往往只是浪费时间因为问题可能根本不在你以为的地方。注意每次只改一个变量改完立刻测试。同时改多个地方一旦问题解决你也不知道是哪个改动起的作用下次遇到还是不会。Codex 的 401 说到底就是身份没对上这一件事但对不上的方式有十几种。把报错文本读透把配置来源理清把测试手段用对这个问题其实一点都不难。我见过太多人卡在这里几个小时最后发现只是变量名少了个下划线或者密钥复制时多带了个空格。希望这篇能帮你少走点弯路。
