沙箱里文件写不进去:三种模式下的可写边界

2026-08-09

用 Codex(OpenAI Codex)干活最容易让人怀疑人生的一幕,是它在终端里讲得头头是道,你切回编辑器一看,文件一个字都没变,git status 干干净净。第二种变体是命令执行完没有任何报错,但你去找生成的文件,找不到。第三种是想让它动工作区之外的东西——隔壁那个仓库、用户目录下的某个配置——直接失败。

这三种现象背后往往是同一件事:沙箱的可写边界跟你以为的不一样。下面按可执行的顺序走一遍排查。

一、先确认沙箱现在处于什么状态

第一条命令不是去改配置,而是看现状:

codex doctor --summary

在 codex-cli 0.147.0(Windows 11)上,这条命令的 Configuration 分组里有一行 sandbox,本机输出是:

restricted fs + restricted network · approval OnRequest

这一行要拆成三段读:restricted fs 表示文件系统有边界,restricted network 表示网络也有边界,approval OnRequest 是当前的审批策略。如果你的写入失败,而这一行显示的是受限状态,那基本可以把怀疑对象锁定在沙箱上了。

同一个分组里还有一行 config。这行值得单独盯一眼,因为它能立刻排除一整类误判。在 codex-cli 0.147.0(Windows 11)上故意传一段语法不合法的 TOML(codex -c 'features=[unclosed' doctor --summary),doctor 并没有崩溃退出,照常跑完,只是多出一行:

✗ config       config could not be loaded - Fix the reported config error, then rerun codex doctor.

这意味着:配置文件写坏了,Codex 不会当着你的面拦住你,它照跑不误,只是你在 config.toml 里加的那个 sandbox_mode 压根没生效。所以「我明明改了配置怎么没用」的第一步,永远是跑 doctor 看这一行。

二、确认这次会话跑在哪种模式

CLI 侧用 -s, --sandbox 指定,取值只有三个。这一点不用查文档,故意写错就能问出来——在 codex-cli 0.147.0(Windows 11)上执行 codex -s bogus-mode

error: invalid value 'bogus-mode' for '--sandbox <SANDBOX_MODE>'
  [possible values: read-only, workspace-write, danger-full-access]

For more information, try '--help'.

这篇之所以要把三种模式的官方释义贴出来,是因为「写不进去」的答案就藏在这三句话的动词差异里:

模式官方原文
read-only”The agent can inspect files, but it can’t edit files or run commands without approval.”
workspace-write”The agent can read files, edit within the workspace, and run routine local commands inside that boundary.”(官方标注这是默认模式)
danger-full-access”The agent runs without sandbox restrictions. This removes the filesystem and network boundaries and should be used only when you want the agent to act with full access.”

关键词是 workspace-write 里的 within the workspace。默认模式下能写的不是「你的整块硬盘」,而是工作区这个边界内部。所以写不进去最常见的成因,不是权限被禁了,而是目标路径压根不在工作区里——你在 A 目录启动,让它去改 B 目录,边界从一开始就画错了。

配置侧对应的键是 sandbox_mode

三、别把审批策略当权限开关

这是我见过最费时间的一个误会:有人发现写入失败,就去把审批策略调成 never,结果照样写不进去,然后开始怀疑是不是装坏了。

approval_policy 三档的官方释义是这样的:

  • untrusted:只有受信任的命令(ls、cat、sed 之类)免审批,其余升级给用户
  • on-request:由模型决定何时请求审批
  • never:从不询问,执行失败直接回传给模型

看清楚 never 那句:它改变的是「要不要问你」,不是「能不能做」。 沙箱边界一条都没动。而且 never 还有个副作用——失败不再弹审批打断你,而是直接把失败结果丢回模型,于是你在终端上更不容易注意到刚才那次写入其实没成功。

真正撤掉边界的是另外两样东西:把沙箱设成 danger-full-access,或者用顶层选项 --dangerously-bypass-approvals-and-sandbox(官方对它的原话是 “EXTREMELY DANGEROUS. Intended solely for running in environments that are externally sandboxed”)。这两条不该是排查的第一反应,往下看还有更温和的办法。

另外还有一个 approvals_reviewer 键,取值 userauto_review;CLI 侧 --approve-for-me 会让审批请求走自动复核,并使用 workspace-write 沙箱。

四、一条不发模型请求就能判定的命令

上面都是推理,接下来是可以直接跑的判定手段。Codex CLI 有个 sandbox 子命令,官方对它位置参数的说明是 “Full command args to run under Windows restricted token sandbox”——也就是拿沙箱包一条普通命令来跑。

在 codex-cli 0.147.0(Windows 11)上,我在沙箱里执行了一条往仓库路径写文件的无害命令(形如 codex sandbox cmd /c "echo hi > <你的仓库路径>\__sbtest.txt"),命令返回之后去 ls 查这个文件,文件不存在。也就是说,默认沙箱状态下对该路径的写入没有落盘,而命令本身并没有把这件事嚷嚷出来。

这条命令的价值在于它不发起任何模型对话请求,纯本地、无害、可重复。当你不确定「到底是沙箱拦了,还是模型根本没动手」,先跑它,答案一目了然。

顺带记一个同样是实测出来的坑:--sandbox-state-* 这几个选项是一组的。单独给 --sandbox-state-disable-network 而不给 --sandbox-state-json,会直接报参数缺失:

error: the following required arguments were not provided:
  --sandbox-state-json <JSON>

必须先有一份 state JSON,才能在它之上做增删。

五、处置:在不撤掉沙箱的前提下扩边界

确认是边界问题之后,官方给的做法有两条,都不需要你把沙箱整个关掉。

命令行临时加目录,用顶层选项 --add-dir <DIR>,它的定位就是「主工作区之外额外可写的目录」。想换工作根目录本身,用 -C, --cd <DIR>

写进配置的话,对应的键是 sandbox_workspace_write.writable_roots——它的作用正是在不撤掉沙箱的前提下扩大可写目录。同一组下还有 sandbox_workspace_write.network_access,控制 workspace-write 下是否出网;如果你的「写不进去」其实伴随着依赖下载失败,问题可能在这一条上。

sandbox_mode = "workspace-write"

[sandbox_workspace_write]
writable_roots = ["<你要额外放开的目录>"]
network_access = false

以上为按官方文档键位组合的示例,未逐项实测,以官方文档为准。

-c 做临时覆盖时有个容易踩的点:-c key=value 里点号表示嵌套路径,value 按 TOML 解析,解析失败则按字面字符串处理。官方给的示例之一是 -c 'sandbox_permissions=["disk-full-read-access"]'——注意整体带的那对单引号。数组如果没写成合法 TOML,它不会报错,而是安静地被当成一个字符串,于是你又得到一次「改了没用」。

六、Windows 侧要单独说

沙箱在不同平台上是三套完全不同的机制:macOS 用系统内置的 Seatbelt,Linux / WSL2 需要用包管理器装 bubblewrap(bwrap),Windows 则是在 PowerShell 中使用原生 Windows 沙箱,不需要 WSL、不需要虚拟机。

这条差异有直接的排查意义:Linux 上「沙箱起不来」第一件事是查 bwrap 装没装;Windows 上完全不适用这套步骤,别照着 Linux 的帖子折腾。

Windows 原生沙箱有两种模式,配置键是 windows.sandbox

模式特征
elevated(官方标注为首选)使用专用的低权限沙箱用户;文件系统权限边界 + 防火墙规则;需要管理员批准的初始化设置
unelevated(回退)用「从当前用户派生的受限 Windows token」运行命令;基于 ACL 的文件系统边界;用环境级离线控制替代防火墙规则;官方明说保护更弱,但在拿不到管理员批准时可用

另有 windows.sandbox_private_desktop,默认 true。版本支持上,Windows 11 是推荐,较新的 Windows 10(v1809+)是 best effort,更老的 Windows 10 不推荐;硬性要求是 winget 必须可用。

初始化失败的常见原因,官方列了三条:拒绝了 UAC 提示、本地用户创建被阻止、防火墙规则被限制。如果你看到错误 1385,官方原话是 “Windows is denying the logon type the sandbox user needs.”,含义是沙箱用户已经建好了,但策略不允许它执行命令。

官方给的排查顺序只有四步:① 重启 Codex ② 重试 elevated 初始化 ③ 需要时回退到 unelevated ④ 发送诊断,日志位于 CODEX_HOME/.sandbox/sandbox.log。到这里为止,超出这四步的注册表改法、组策略改法,官方文档没给,本文也不编。

还有两个 Windows 侧的现象值得记住:某些任务会故意在无出网的状态下运行,取决于权限模式;如果某个目录是「writable by Everyone」(对所有人可写),Codex 会告警,提示 Windows 权限过宽——这个告警是冲着你的目录 ACL 去的,不是 Codex 出了故障。

本机的旁证:在 codex-cli 0.147.0(Windows 11)上,~/.codex/ 下确实存在 .sandbox/.sandbox-bin/.sandbox-secrets/ 三个目录,config.toml[windows] sandbox = "elevated"

七、改完怎么验证

按这个顺序回验,不要跳:

  1. 再跑一次 codex doctor --summary,先看 config 行是不是 ok——配置没加载,后面全白搭;再看 sandbox 行有没有变成你预期的状态。
  2. 再跑一次第四节那条 codex sandbox 写文件命令,目标换成你刚放开的那个目录,然后 ls 查文件在不在。这一步是纯本地的,不消耗任何模型请求。
  3. 别指望 --strict-config 帮你兜住拼写错误。在 codex-cli 0.147.0(Windows 11)上,codex -c model_reasoning_effortt=high --strict-config exec --help 正常打印了 help,没有报未知字段错误——说明它的校验发生在真正加载配置去跑会话的时候,--help 这类不进入会话的路径不触发。

八、什么情况说明不是沙箱的锅

一条道走到黑最费时间,下面几种现象长得很像,但成因在别处:

doctor 的 config 行报错。 那就先修配置,改沙箱模式是无效动作。

版本对不上。 在 codex-cli 上排查任何行为差异之前,先跑 codex --version 取实时值。本机采集时遇到过一次:开头执行得到 codex-cli 0.131.0,十几分钟后同一台机器同一个可执行文件得到 codex-cli 0.147.0——Codex 具备自更新能力(配置里 check_for_update_on_startup 默认 true),所以「我昨天看到的不是这样」是正常现象,别用记忆里的版本号当依据。另外,官方排查条目里也提到 CLI 与桌面应用可能是两个不同的版本,需要分别查(官方文档口径,非本机实测)。

文件其实改了,是你看错了面板。 官方排查条目提到:项目在 Git 仓库里时,桌面应用侧栏展示的是全部 Git 状态变更,官方给的做法是把 diff 面板切到 “Last turn” 视图,只看本轮改动。这条属于桌面应用,本文未做实测。

worktree 上跑不起来。 官方给的原因是 worktree 是另一个目录、继承了 Git 文件但缺依赖,做法是通过本地环境跑初始化脚本,或用 .worktreeinclude 把被忽略的文件纳进来。跟沙箱无关。

项目级配置识别不到。 官方说法是配置没放在 .codex 文件夹里,要确保 .codex 文件夹在项目根;monorepo 要打开正确的目录。

别拿实验阶段的开关当解药。codex features list 能看到每个特性所处的阶段。在 codex-cli 0.147.0(Windows 11)上,跟网络边界沾边的 network_proxy 处于 experimental 阶段、生效值 falseuse_legacy_landlock 处于 deprecated 阶段。这一列还有个容易误读的地方:removed 阶段的特性仍然会出现在列表里,而且部分 removed 项的生效值是 true——它指的是这个开关本身不再需要你控制、行为已经固化,不等于功能没了。

把顺序记住就行:doctor 看状态 → codex sandbox 判定落盘 → 分清模式和审批 → 用 --add-dir / writable_roots 扩边界 → 回验。真要动 danger-full-access,放到最后,而且要清楚自己在放弃什么。

相关阅读


本文依据 Codex 官方文档(learn.chatgpt.com/docs/ 的《Sandbox》《Windows sandbox》《Troubleshooting》页面)整理,核对日 2026-08-09;文中标注「本机实测」的部分基于 codex-cli 0.147.0 / Windows 11 环境下的只读命令输出。产品功能、模型与价格以官方最新说明为准。桌面应用与云端部分为官方文档口径,非本机实测。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。