Codex patch 和现有代码冲突:分清两种报错再修

Codex apply patch 时报「Invalid Context」或「No such file or directory」——先分清是哪一种,再决定重读文件还是重启 session。

Codex 想改一个文件,apply_patch 这一步挂了。你会看到两种完全不同的报错,修法也完全不同:

  • apply_patch verification failed: Invalid Context(经常打印成 Error: Invalid Context: 后面跟一行 @@)——patch 周围的 context 行和文件对不上了。这才是真正的 state drift。
  • Failed to apply patch ... No such file or directory / execution error: Io(Os { code: 2, kind: NotFound })——文件本身没问题,是 Codex CLI 运行时丢了 apply_patch 辅助程序的路径。这是 CLI 的已知 bug(截至 2026 年 6 月,影响 0.117–0.120),根本不是冲突。

最快修法(Invalid Context):停下,重读当前文件,重新生成 patch。最快修法(No such file or directory):重启 Codex session——别动你的代码。

下面教你怎么分清自己是哪一种,以及怎么防止复发。

Codex 的 patch 和 git apply 不一样

这一点很关键,因为「行号对不上」的直觉在这里是错的。Codex CLI 不输出带行号的 unified diff——它用的是 OpenAI 的 V4A 格式,通过 apply_patch 工具落地。V4A 的 hunk 是靠 context 锚定的:它靠匹配改动周围的三行代码(以及可选的 @@ class/function 头)来定位,而不是靠行号。格式长这样:

*** Begin Patch
*** Update File: src/utils.ts
@@ export function parse(input) {
   const trimmed = input.trim();
-  return JSON.parse(trimmed);
+  return JSON.parse(trimmed || "{}");
 }
*** End Patch

因为靠 context 锚定,行号差几行不会挂——但那三行 context 一旦被改就会挂。所以一次 reformat、一处顺手改动破坏性才这么大:它正好改写了 Codex 用来匹配的那几行。CLI 这时报 Invalid Context,拒绝瞎猜。(规范见 openai/codex apply_patch 说明。)

先分清:你看到的是哪种报错?

报错字符串归类最快修法
Invalid Context / Error: Invalid Context: / Failed to find context '...' in <file>context 漂了(文件变了)重读文件,重新生成 patch
patch 批准后才报 No such file or directory / Io(Os { code: 2, kind: NotFound })CLI 运行时路径 bug重启 Codex session
Update hunk does not contain any lines / parse 报错模型给的 patch 残缺重新要一份完整 hunk
文件里留下 <<<<<<< 标记git merge 留的,不是 Codex先解 git 冲突,再重跑 Codex

如果你落在 CLI 运行时路径 bug 这一行,直接跳到 「No such file or directory」运行时 bug——所有 state drift 的步骤都不适用。

常见原因(context 漂移类)

按命中率从高到低。这些都会报 Invalid Context

1. 文件在 Codex 读和 patch apply 之间变了

Codex 30 秒前读了 utils.ts,之后你在编辑器里 format 了一下(Prettier on save)。patch 里的 context 行现在对不上了。

如何判断git status 看文件的修改时间是不是出乎意料地新;或 git diff src/utils.ts 看到你没打算改的变动。

2. autoformat 在 Codex 读之后、apply 之前跑了

VS Code 的「format on save」是隐形元凶。你只是切了一下窗口,但失焦保存触发了 Prettier,Codex 的 context 锚现在差了空白或换行。

如何判断:关掉 format-on-save("editor.formatOnSave": false),让 Codex 重读 + 重生成——能 apply 就是它。

3. 空白 / 行尾不匹配(CRLF vs LF)

Windows clone repo 用了 core.autocrlf=true,本地是 CRLF,Codex 的 context 行是 LF。每行都差一个不可见的回车,context 永远匹配不上。

如何判断git config core.autocrlftrue;或 file src/utils.tsCRLF line terminators

4. Codex 读时和 apply 时不是同一个 commit

Session 开始时在 feature/billing,思考期间你切回了 main,又让它 apply。patch 的 context 属于老分支,新分支没有那些行。

如何判断git log --oneline -5 看两个分支,分叉点在 Codex 读的文件之前——patch 对的是另一个过去。

5. 两个 agent(或 agent + 你)并行编辑同一文件

Claude Code 和 Codex 同时被让改 utils.ts,其中一个先完成,另一个的 patch 锚在改之前的状态上,冲突。

如何判断git log src/utils.ts --oneline 多了你不记得的近期 commit,或 git status 显示你没改过的未提交内容——协调失败。

6. Codex 给的是模糊 / 省略 patch

模型偶尔会给带 // ... 占位符的 hunk,或只给一行 context 而不是三行。V4A 解析器就没法唯一定位改动(Update hunk does not contain any lines,或者悄悄匹配错位置)。

如何判断:patch 文本里有字面 // ...... (unchanged),或每个 hunk 的 context 不足三行。让它给完整 hunk。

最短修复路径(context 漂移类)

按收益从高到低。Step 1 一步就能覆盖大部分 Invalid Context

Step 1:停 → 看现状 → 重生成

看到 Invalid Context 时:

# 1. 看实际状态
git status
git diff src/utils.ts

# 2. working tree 有意外,找来源
git log --oneline -5 src/utils.ts

然后告诉 Codex:

The patch failed with "Invalid Context". Re-read src/utils.ts from disk
right now, then regenerate the patch against the current contents.

别硬塞过期 patch。让 Codex 重读文件是官方设计的恢复路径——apply_patch 工具特意把失败报得很明确,就是为了让模型能重读、重试。

Step 2:agent session 期间关 autoformat

VS Code 设置:

{
  "editor.formatOnSave": false,
  "editor.formatOnPaste": false,
  "editor.codeActionsOnSave": {}
}

.vscode/settings.json 按 workspace 配。agent 完事后再打开(顺手跑一次 formatter 归一化)。

Step 3:repo 范围统一行尾

.gitattributes

* text=auto eol=lf
*.bat text eol=crlf
*.png binary
*.jpg binary

然后对现有 checkout 重新归一化:

git add --renormalize .
git commit -m "Normalize line endings to LF"

这样所有人的工作区都是 LF,Codex 的 context 行也对得上了。Windows 开发者请设 git config --global core.autocrlf input(不是 true)。

Step 4:agent 跑前 commit,跑完再 commit

单 agent 纪律:

1. 启动 agent 前 git status 干净。
2. agent 改 + 测。
3. 下一个 session 前 git add -A && git commit -m "agent: <task>"。

多 agent 工作流,共享文件按「独占」对待——同一时间只有一个 agent 改某个文件,通过 task tracker 或 CODEOWNERS-style claim 协调。

Step 5:强制完整 hunk,不要省略

Prompt 里写:

Return the full patch text — every changed line plus 3 lines of real
context above and below each hunk. Do NOT use `// ...` placeholders,
`// (unchanged)` markers, or fewer than 3 context lines.

三行 context 是 V4A 的默认值;明确要回来,就能避免那些锚得不够、匹配到错位置或解析失败的 patch。

Step 6:持续冲突就拆 patch

一份大 patch 反复挂,拆成原子小 patch:

Don't generate one big patch. Instead:
1. Add the new validate() method — return that patch.
2. After I apply, update the call sites — return that patch.
3. After I apply, update the tests — return that patch.

Each patch should touch at most 3 files.

小 patch 的 blast radius 也小,底下的文件漂了也好回。

「No such file or directory」运行时 bug

如果 patch 已经批准、然后才报 No such file or directoryexecution error: Io(Os { code: 2, kind: NotFound })、或 bwrap: Can't mkdir ...: No such file or directory,这就不是代码冲突。这是 Codex CLI 的一个已知运行时回归(截至 2026 年 6 月,0.117–0.120 都有报告):CLI 缓存了一个会失效的 apply_patch 辅助程序路径(在 ~/.codex/tmp/arg0 下),session 中途丢了,于是一分钟前还能 apply 的 patch 现在撞上 ENOENT。

恢复办法,按顺序试:

  1. 重启 Codex session。 退出再重开 codex,辅助程序路径会被重新解析,同一份 patch 就能 apply。大多数人这一步就好了。
  2. 更新 CLInpm install -g @openai/codex@latest(或 brew upgrade codex)。新版已经修了一些,别跟一个已经修好的 bug 死磕。
  3. Windows 上,大 patch 还可能因为 patch body 是通过 argv 传的、超过命令行长度上限而失败。把改动拆小(上面的 Step 6),或在 WSL2 里跑 Codex。

实时状态看 issue 区:openai/codex issues

怎么确认修好了

重跑之后,确认改动真的落地了:

git diff src/utils.ts          # 你要的改动在
git status                     # 没有残留 <<<<<<< 标记,没有意外文件
npm test                       # 或你项目的测试命令

如果 git diff 显示了预期改动、测试也过,就是干净 apply 了。如果还看到 <<<<<<< 标记,那是 git merge 遗留的冲突——手动解掉、commit,再对干净的状态重跑 Codex。

常见问题

为什么 Codex 说 diff 是对的,git 却说文件不一样? 两边都对。Codex 的 V4A patch 锚在它读到的那一刻的文件上;之后磁盘上的文件变了(formatter、切分支、或另一个 agent)。patch 不是错,是过期了。重读、重生成即可。

Invalid Contextgit merge 冲突是一回事吗? 不是。Invalid Context 来自 Codex 的 apply_patch 找不到锚行,靠重新生成 patch 解决。<<<<<<< 标记来自 git merge / git rebase,得在文件里手动解。

patch 刚批准就反复失败,是我的仓库坏了吗? 基本不是。如果报的是批准后的 No such file or directory,那是 CLI 的「路径失效」运行时 bug。重启 Codex session 和/或更新到最新版即可,你的代码没被动过。

关 format-on-save 真有那么重要? 比对 git apply 重要得多。因为 V4A 是精确匹配三行 context,一次重排或重缩进哪怕逻辑完全一样,也会破坏锚。agent session 期间关掉它,最后再统一 format 一次。

怎么防止两个 agent 互相覆盖对方的 patch? 一个任务期间把每个文件当成独占:一个 agent 改完、commit,然后下一个 agent 才读。通过 commit 或 CODEOWNERS-style claim 协调归属——对同一文件并行编辑是制造 Invalid Context 最稳的方式。

预防建议

  • .gitattributes* text=auto eol=lf)一次性统一行尾。
  • agent session 期间关 editor.formatOnSave,结束时刻意 format 一次。
  • 共享文件按独占对待——同一时间一个 agent,通过 commit 协调。
  • 始终要求完整 hunk 加三行真 context;锚得不够的 patch 省的 token 远抵不上冲突的代价。
  • 每次 agent run 前后都 commit;干净进、干净出。
  • 保持 Codex CLI 更新,别撞上一个已经修好的运行时 bug。
  • 大重构拆成小 patch 链,不要一份巨型 patch。

相关阅读

标签: #Codex #Coding Agent #排查 #排查 #Patch 冲突