改完 `config.toml` 不生效:按这四层覆盖顺序往下查
改了 ~/.codex/config.toml,保存,重开一个会话,行为跟没改一样——这是 Codex(OpenAI Codex)用户最常撞的一类问题。它烦人的地方在于:没有任何报错。你只能对着配置文件反复看,怀疑是键名拼错了,然后把值改来改去试。
大多数时候,键名没拼错。问题出在配置的覆盖层上:你写的那个值在生效之前,被前面某一层截胡了;或者那个文件压根就没被读进去。
下面这四层要按顺序查。顺序是有讲究的——第一层没排除掉就去查第三层,你会得到一堆自相矛盾的观察结果。
第一层:这个配置文件到底加载成功了没有
这一层排第一,是因为它最容易被跳过,而它一旦出问题,后面三层查什么都是白费。
判定命令(只读,安全):
codex doctor --summary
看输出里 Configuration 分组的 config 那一行。正常是 config (loaded)。
这里有一条本机实测的结论,值得单独拎出来说。在 codex-cli 0.147.0(Windows 11)上,我故意喂了一段语法不合法的 TOML:
codex -c 'features=[unclosed' doctor --summary
命令没有崩溃退出,doctor 照常从头跑到尾,只是在 Notes 区多出了一行:
✗ config config could not be loaded - Fix the reported config error, then rerun codex doctor.
这个设计很反直觉:配置坏了,CLI 不会拒绝启动,它会带着一份空配置继续跑。所以你看到的”改完不生效”,可能压根不是”这个键不生效”,而是”整个文件都没加载”。这里要说清楚边界:本机验证过的是数组没闭合这一种,而且走的是命令行 -c 覆盖这条路径;其它 TOML 语法错误(以及写在 config.toml 文件里的语法错)是否同样只在 doctor 的 config 行里体现,我们没有逐一试过。但反过来是成立的——只要这一行是 ✗,就说明配置没读进去,不必再往下查了。
除了语法错,还有两种”文件没被读”的情况:
- 你编辑的不是它读的那个文件。 官方 Configuration Reference 页写明配置位置是
$CODEX_HOME/config.toml,默认~/.codex/config.toml。如果CODEX_HOME被指向了别处(容器、CI 镜像、多账号切换脚本里很常见),你手改的那个文件跟它读的就不是同一个。 - 命令本身要求不加载。
codex exec --ignore-user-config的官方说明是不加载$CODEX_HOME/config.toml;注意它有个细节——auth 仍然使用CODEX_HOME。也就是说登录状态是好的,配置是空的,从表面症状看特别像”配置不生效”。
处置:修掉 TOML 语法问题,或者把 CODEX_HOME / 启动命令理顺。
处置后怎么验证:重跑 codex doctor --summary,那一行从 ✗ 变回 ✓ config (loaded) 才算过。doctor 的状态符号有四种:✓(ok)、○(idle)、⚠(notes/warn)、✗(fail),结尾还有一行统计,形如 17 ok · 1 idle · 1 notes · 0 warn · 0 fail。要把诊断结果贴给同事或者贴进 issue,用 codex doctor --json——官方说明是 “Emit a redacted machine-readable report”,是脱敏的。
第二层:命令行上的一次性覆盖
配置加载成功了,但你这次启动时在命令行上带的参数,优先级在配置文件之上。
最常见的三类:
-c, --config <key=value>:覆盖~/.codex/config.toml里的值。点号路径表示嵌套(foo.bar.baz)。官方给的三个例子是-c model="o3"、-c 'sandbox_permissions=["disk-full-read-access"]'、-c shell_environment_policy.inherit=all。--enable <FEATURE>/--disable <FEATURE>:可重复,官方明说等价于-c features.<name>=true/=false。所以你在配置文件里关掉的特性,可能被启动脚本里的一个--enable又打开了。-s, --sandbox <SANDBOX_MODE>、-m, --model <MODEL>这类专用选项,同样是本次会话覆盖。
-c 这里藏着一个很阴的行为:value 按 TOML 解析,解析失败则按字面字符串处理。它不报错,它给你降级。所以 -c 后面写了个本该是布尔或数组的值但引号没配对,你拿到的会是一个字符串,而不是一条错误提示。
判定方法:把你实际敲的那条命令原样看一遍——包括 shell 别名、封装脚本、任务计划里的那条。然后把这些参数全部去掉,只跑最裸的启动方式,看行为是否回到配置文件里写的样子。
反过来也能用:把你正在纠结的那个键放到命令行上跑一次,如果这时行为变了,说明键名和取值都是对的,问题就在”配置文件那一侧的加载/覆盖”,可以直接回第一层和第三层。
第三层:profile 叠加层
这一层是多环境用户的重灾区。
-p, --profile <CONFIG_PROFILE_V2> 的官方说明是:把 $CODEX_HOME/<name>.config.toml 叠加到基础用户配置之上。也就是说你的配置根本不在一个文件里——基础的 config.toml 一份,profile 一份,后者压前者。你在 config.toml 里改的键,如果 profile 文件里也写了同名键,你的修改就被盖掉了。
这里还有一个特别容易搞混的地方:-p 和 -P 是两套完全不同的东西。
-p, --profile:上面说的配置层叠加。-P, --permission-profile <NAME>:套用配置栈里的命名权限档,对应配置里的permissions.<name>.*那一整块(extends、workspace_roots、filesystem、network等),另有default_permissions指定默认档名。
改权限相关的键没生效时,先确认自己改的是哪一套。
另外,企业机器上还有一层你改不动的:官方 Configuration Reference 里,auto_review.policy 标注了「受管配置优先」,guardian_policy_config 是「受管 Markdown 评审策略,覆盖本地策略」。如果你在公司发的设备上折腾这两块,本地怎么写都可能被上层策略盖掉。
验证:去掉 -p 参数跑一次做对照。两次行为不同,就锁定在 profile 层。
第四层:这个键在你这台机器上本来就不生效
前三层都排除了,还有最后一种可能:键写对了、文件加载了、没人覆盖你,但这个键在你这个平台或这个版本上就是不起作用。
平台差异。 最典型的是 features.unified_exec,官方标注默认 true,但括号里跟了一句 Windows 除外。在 codex-cli 0.147.0(Windows 11)上跑 codex features list,unified_exec 的生效值确实是 false,和文档口径对得上。你在 Windows 上把它写成 true 然后期待行为变化,方向就错了。Windows 侧还有几个专属键:windows.sandbox(取值 unelevated 或 elevated)、windows.sandbox_private_desktop(默认 true),以及 hooks 里的 commandWindows(仅 Windows 的命令覆盖)。
特性阶段。 codex features list 输出三列:特性名、阶段、当前生效值。在 0.147.0 上观测到的阶段共五种:stable、under development、experimental、deprecated、removed。几个直接影响判断的例子(同样是 0.147.0 / Windows 11 的实测行):
| 特性 | 阶段 | 生效值 |
|---|---|---|
code_mode | under development | false |
token_budget | under development | false |
network_proxy | experimental | false |
web_search_cached | deprecated | false |
steer | removed | true |
前两行是 under development,第三行是 experimental——这类开关别当稳定功能用,配了没动静是正常的。
deprecated 那一行则指向另一个高频错法:features.web_search / web_search_cached / web_search_request 这三个键官方已标注均已弃用,要改用顶层 web_search。顶层 web_search 默认值是 cached,取值为 disabled / cached / indexed / live——默认既不是关闭也不是实时。你在 features 表里配了半天搜索行为没变,多半就是配在了废弃键上。CLI 侧开实时搜索对应的是 --search。
removed 这一行最容易误读:removed 阶段的特性仍然会出现在 list 里,而且部分 removed 项的生效值是 true(比如 steer)。这说明 “removed” 指的是这个开关本身不再需要你控制、行为已经固化,不等于功能没了。看到 removed 就去配置里加一行想把它打开,是白做工。
配置内部还有覆盖关系。 MCP 那块,disabled_tools 官方明确写着在 enabled_tools 之后生效。两个都配的时候以 deny 为准——你在 enabled_tools 里加的工具,会被 disabled_tools 里的同名项吃掉。
处置后统一怎么验证
改完别急着开会话试感觉,先用只读命令把”生效值”看出来:
| 你改了什么 | 用什么看 | 看哪里 |
|---|---|---|
| 任何配置 | codex doctor --summary | Configuration 组的 config 行 |
sandbox_mode / approval_policy | codex doctor --summary | Configuration 组的 sandbox 行 |
features.* | codex features list | 第三列「生效值」 |
mcp_servers.* | codex mcp list | Status / Auth 列 |
在 codex-cli 0.147.0(Windows 11)上,doctor 的 sandbox 行输出形如 restricted fs + restricted network · approval OnRequest,沙箱模式和审批策略一眼就能对上,比开个会话去感受靠谱得多。codex mcp list 的表头是 Name | Command | Args | Env | Cwd | Status | Auth,其中 Env 列的值会被打成 *****、只显示键名;但 Command / Args / Cwd 三列是原样输出,可能带上你的本地路径,往外贴之前自己再过一眼。
还有一条前置动作:每次排查先跑 codex --version。本机实测过一个挺离谱的场景——同一台机器上,采集开头 codex --version 是 codex-cli 0.131.0,十几分钟后再敲同一条命令变成了 0.147.0,which -a codex 全程只有一个可执行文件。Codex 有自更新能力(配置里 check_for_update_on_startup 默认 true),所以”昨天的版本号”不能拿来当排查依据,一切以当次 codex --version 的实时输出为准。默认值和特性阶段都会随版本变。
什么情况说明不是配置覆盖的问题
别一条道走到黑。出现下面几种迹象,就该换方向了:
1. doctor 显示 config (loaded),且 codex features list 里那一项的生效值确实是你写的值。 配置层已经清白了,接着查的应该是这个键本身管的是不是你以为的那件事。
2. 加了 --strict-config 没报错,不代表键名拼对了。 --strict-config 的官方说明是 config.toml 里出现本版本不认识的字段时直接报错退出。但在 0.147.0(Windows 11)上,我故意把键名拼错跑了一次:
codex -c model_reasoning_effortt=high --strict-config exec --help
结果是正常打印 help,没有任何未知字段报错。说明这项校验发生在真正加载配置去跑会话的路径上,--help 这种不进入会话的路径不触发。所以别把它当成”任何情况下都能拦住拼写错误”的保险。
3. MCP server 起不来,先别怀疑配置写错。 startup_timeout_sec 默认只有 10 秒,慢启动的 server 必须显式调大;required 为真时,启用的 server 初始化失败会让整个启动失败。这些是超时和依赖问题,不是覆盖问题。
4. 沙箱里写文件没落盘,那是沙箱在干活。 在 0.147.0(Windows 11)上实测:用 codex sandbox 执行一条把内容重定向到仓库内文件的命令,命令返回后目标文件并不存在。这是默认沙箱状态下的预期行为,跟 config.toml 有没有生效是两回事。
5. 现象只出现在桌面应用或 IDE 上。 官方 Troubleshooting 页里有一条:功能在 CLI 有、桌面应用没有,原因是两个面的 Codex 版本不同,官方给的做法是分别查版本——CLI 用 codex --version,macOS 应用用 /Applications/Codex.app/Contents/Resources/codex --version。桌面应用与 IDE 扩展我们没有实测,这里只转述官方口径。
一段可以直接抄的对照配置
想快速验证”这一层通不通”,可以先把配置压到最小,只留你正在排查的那几个键:
model_reasoning_effort = "high"
sandbox_mode = "workspace-write"
[sandbox_workspace_write]
network_access = false
[windows]
sandbox = "elevated"
[mcp_servers.example]
command = "node"
args = ["server.js"]
startup_timeout_sec = 120
其中 [windows] sandbox = "elevated" 与 startup_timeout_sec = 120 是本机 config.toml 里实际存在的写法。以上为按官方文档键位组合的示例,未逐项实测,以官方文档为准。
写完保存,按顺序过一遍:codex doctor --summary 看 config 与 sandbox 两行 → codex features list 看生效值 → codex mcp list 看 Status。三处都对上,才算这次修改真的落地了。
相关阅读
- Codex
config.toml全景:它在哪、有几层、该先改哪几个键 - Codex CLI 的
-c到底覆盖了什么:四条规则与一次配置加载失败的实测 - Codex CLI 的
--profile:给不同项目挂不同配置层 - Codex 的六个使用面:一张图看懂该用哪个
本文依据 Codex 官方文档(learn.chatgpt.com/docs/ 的《Configuration Reference》《Troubleshooting》页面)整理,核对日 2026-08-09;文中标注「本机实测」的部分基于 codex-cli 0.147.0 / Windows 11 环境下的只读命令输出。产品功能、模型与价格以官方最新说明为准。桌面应用与云端部分为官方文档口径,非本机实测。