# Claude Code Error writing file 报错：Ctrl+O 看原文，按原因修复

> Error writing file 只是标签，按 Ctrl+O 看原文：读后被改要重读，目录不可写改权限，参数被截断改分块写，Error editing file 同理。

- URL: https://blog.laozhang.ai/zh/posts/claude-code-error-writing-file
- Published: 2026-10-16
- Updated: 2026-10-16
- Author: LaoZhang AI Team (https://blog.laozhang.ai/zh/about)
- Topic: Claude Code
- Tags: Claude Code, Error writing file, Error editing file, 报错排查, Write 工具

---
`Write(路径) ⎿ Error writing file` 这行红字只是对话视图里的简短标签，不是报错原因。Claude 实际收到的是一段更具体的原文，例如 `File has been modified since read…`、`EACCES: permission denied…` 或 `InputValidationError: Write failed…`。先按 `Ctrl+O` 打开 transcript 视图，找到失败的那次 Write 读原文，再判断该改环境、让 Claude 换写法，还是属于已知 bug。

最常见的三种原文可以直接处理：

- `File has been modified since read`：文件在 Claude 读过之后被编辑器、格式化工具或 hook 改过。让 Claude 重新读取再写；同一个文件反复出现，就找出那个在改文件的程序。
- `InputValidationError … content is missing`：一次要写的内容太长，工具调用的参数被截断了。按 Esc 打断重试，改成先写骨架、再分块补内容。
- `EACCES` 或 `EPERM`：目录没有写权限，或者 macOS 不允许终端访问桌面、文稿、下载文件夹。Claude Code 的权限模式放行不了这一类，要改目录权限或系统设置。

Claude 自己重试一次就成功的，通常不用管，[Claude Code 报错文档](https://code.claude.com/docs/en/errors#tool-errors)的工具错误一节开头就写着大多数工具错误 Claude 会自行纠正。下文的本机结果来自 2026 年 10 月 16 日在 Claude Code 2.1.295 桌面版（macOS，Opus 5.5）上跑的 9 个场景；Windows 分支和输出截断没有在本机复现，引用的是 GitHub issue 里的报告。

## `Error writing file` 背后的原文：按 Ctrl+O 打开 transcript 视图

终端里的默认对话视图会把每次工具调用压缩成一两行，失败时就是这样：

```text
● Write(app/Mcp/Tools/GetMyTasksTool.php)
  ⎿ Error writing file
```

改已有文件用的是 Edit 工具，界面显示为 `Update(路径)`，失败时那一行变成 `Error editing file`。官方报错文档在讲文件编辑被拦截时写得很直接：对话视图只显示一行简短的 `Error editing file`，完整信息要在 `Ctrl+O` 打开的 transcript 视图里看。[Claude Code 交互模式文档](https://code.claude.com/docs/en/interactive-mode)对 `Ctrl+O` 的说明是切换 transcript viewer，显示详细的工具调用和执行过程。

具体做法：

1. 看到红字后按 `Ctrl+O`，往上找到失败的那次 `Write(...)` 或 `Update(...)`，读它下面的工具结果原文。再按一次 `Ctrl+O` 回到对话视图。部分终端的快捷键可能不同，以 `?` 调出的快捷键面板为准。
2. 记下三样东西：原文第一行、报错里的路径、`claude --version` 的输出。路径末尾如果多出 `.tmp.` 加一串数字和字母，那是 Write 的临时文件，说明问题出在目录上（见下文权限一节）。
3. 用 VS Code、JetBrains 插件或桌面版时，界面位置不同。找不到详情时，可以直接让 Claude 把上一次 Write 收到的错误原文原样贴出来，这段文字本来就在它的上下文里。

官方报错文档的错误索引里没有 `Error writing file` 这一条，因为它只是外壳。查文档或搜 GitHub issue 时，要拿 transcript 里的原文去搜。

## 原文对照表：每种报错对应的原因和处理

| transcript 里的原文（开头部分） | 原因 | 处理 |
| --- | --- | --- |
| `File has been modified since read, either by the user or by a linter` | Claude 读过之后，文件被编辑器、格式化工具、hook 或别的进程改过 | 让 Claude 重新读取再写；反复出现就关掉改文件的程序 |
| `File has been unexpectedly modified` | 旧版本的同类报错；Windows 上文件被其他进程锁住时也会显示这一句 | 同上，并检查是否有程序占用该文件 |
| `EACCES: permission denied, open '….tmp.35734.7a96eba60d51'` | 目标所在的目录不可写 | 修目录权限，或换到可写目录 |
| `EPERM: operation not permitted` | macOS 隐私设置不允许终端访问桌面、文稿、下载或 iCloud 云盘 | 在系统设置里给终端授权，或把项目移出这些文件夹 |
| `is a directory, not a file. To create a file inside it, include the file name in file_path.` | 写入路径指向的是目录 | 路径里带上文件名 |
| `InputValidationError: Write failed … The required parameter content is missing` | 工具调用参数缺失，多半是输出被截断 | 打断重试，先写骨架再分块补 |
| `API Error: Claude's response exceeded the ... output token maximum` | 这一轮回复超过输出 token 上限 | 回复 `continue`，或要求一次只写一个文件、一节内容 |
| `String to replace not found in file.` | Edit 的 `old_string` 和文件内容对不上 | 让 Claude 重新读取，按当前内容重写 `old_string` |
| `Found 2 matches of the string to replace, but replace_all is false.` | Edit 的 `old_string` 在文件里出现不止一次 | 带上更多上下文，或明确要全部替换 |
| `File is not valid UTF-8. It may use a legacy encoding such as Windows-1252, Shift-JIS or GBK, or be binary` | 文件是 GBK 等旧编码或二进制，Edit 拒绝修改 | 用保留编码的命令改，或先转成 UTF-8 |
| `Refusing to write … it is a symbolic link. Write to the link's target path instead` | 目标是软链接 | 让 Claude 写链接指向的真实路径 |
| `This write was blocked because the path is spelled in a form that cannot be safely resolved` | worktree 隔离会话里，路径无法安全解析 | 通常 Claude 会自己换成直接路径重试 |
| `… is full (ENOSPC)` 或 `(EDQUOT)` | Claude Code 临时目录所在磁盘或配额满了 | 清理空间，或用 `CLAUDE_CODE_TMPDIR` 换到其他磁盘 |
| `Command failed: cygpath -u '…'` | Windows Git Bash（MINGW64）下的路径转换失败 | 见下文 Windows 一节 |
| 没有更具体的原文，文件其实已写好 | 已知的误报 bug | 核对文件内容，确认无误后让 Claude 继续 |

表中 `modified since read`、`EACCES`、"路径是目录"和 Edit 的两条匹配报错在本机复现过；`EPERM`、编码、软链接、worktree、磁盘已满和输出上限的原文来自官方报错文档；`unexpectedly modified`、参数缺失、cygpath 和误报来自 GitHub issue。

![按原文第一行把报错分成改环境、改写法、已知 bug 三类，并列出应停止重试的三种情况](https://blog.laozhang.ai/posts/zh/claude-code-error-writing-file/img/error-message-fix-map.webp)

## `File has been modified since read`：重新读取，循环时找出改文件的程序

完整原文是：

```text
File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.
```

本机复现方法：让 Claude 读取一个文件，再用 Bash 往这个文件里写入别的内容，然后让 Claude 用 Write 覆盖，就会得到这句原文。Claude Code 发现文件在上次读取之后变过，就拒绝整文件覆盖，避免冲掉别人的修改。

常见的触发者：

- **编辑器或 IDE**：GitHub issue [#10498](https://github.com/anthropics/claude-code/issues/10498) 报告在 macOS 上，IDE 只是打开或预览文件，改变了文件的修改时间，内容没变，Write 也被拦下。报告时间是 2025 年 10 月，版本为 2.0.x。
- **保存时自动格式化、lint 工具的自动修复、watch 进程**：它们在 Claude 读完后改动了文件。
- **编辑后跑格式化的 hook**：`PostToolUse` hook 在 Claude 每次写完后格式化文件，下一次整文件 Write 就会看到"读后被改"。hook 怎么配置见 [Claude Code Hooks、Skills 与斜杠命令：区别、选型和最小配置](https://blog.laozhang.ai/zh/posts/claude-code-hooks-slash-commands-skills)。
- **文件被其他进程锁住**：[#10245](https://github.com/anthropics/claude-code/issues/10245) 指出，在 Windows 上文件被锁时，旧版本给出的是容易误导人的 `File has been unexpectedly modified`；[#11463](https://github.com/anthropics/claude-code/issues/11463) 记录了 Unity 项目里这个报错的反复循环。

处理顺序：

1. 出现一两次不用管，Claude 会重新读取后再写。
2. 同一个文件连续失败三次以上，按 Esc 打断。关掉这个文件的编辑器标签和预览，暂停 format on save 和 watch 进程，再让 Claude "重新读取这个文件后再写"。
3. 只改局部时让 Claude 用 Edit，不要整文件 Write。[Claude Code 工具参考文档](https://code.claude.com/docs/en/tools-reference#edit-tool-behavior)写明，从 v2.1.208 起，文件在读取后被改过，只要 `old_string` 和当前内容精确、唯一地匹配，Edit 仍然可以应用。整文件 Write 在 2.1.295 上仍会被拦（本机测试 4）。

## `EACCES` 与 `EPERM`：Write 看的是目录权限，`chmod 444` 挡不住

在一个权限为 555（不可写）的目录里新建文件，本机得到的原文是：

```text
EACCES: permission denied, open '/…/rodir/new.txt.tmp.35734.7a96eba60d51'
```

报错的路径不是 `new.txt`，而是 `new.txt.tmp.35734.7a96eba60d51`。从这个文件名可以看出，Write 先在同一目录里写一个临时文件，再替换成目标文件，所以真正需要的是目录的写权限。

反过来的测试结果也印证了这一点：把一个文件设为 `chmod 444`（只读），所在目录保持可写，读取后再用 Write 覆盖，结果覆盖成功，文件权限仍显示 `-r--r--r--`。如果你给某个文件设了只读，却发现 Claude 照样改了它，原因就在这里。

![Write 先在同一目录写临时文件再替换目标：目录 555 时第一步就报 EACCES，文件 444 而目录可写时照样覆盖成功](https://blog.laozhang.ai/posts/zh/claude-code-error-writing-file/img/write-temp-file-permissions.webp)

想让 Claude 不碰某些文件，用 Claude Code 自己的 deny 规则，而不是文件权限位。`Edit(...)` 规则同时作用于 Edit、Write 和 NotebookEdit；`Read(...)` 的 deny 规则也会拦下对同一路径的 Edit 和 Write，连新建文件都不行（Write 需要 v2.1.228 及以上）。例如在项目的 `.claude/settings.json` 里：

```json
{
  "permissions": {
    "deny": ["Edit(/config/**)"]
  }
}
```

路径的匹配写法见 [Claude Code 权限文档的 Read 和 Edit 规则](https://code.claude.com/docs/en/permissions#read-and-edit)。

遇到 `EACCES` 时这样查：

```bash
ls -ld 目标所在目录
```

目录属于你自己，用 `chmod u+w 目录` 加上写权限；属主是 root（比如以前用 sudo 跑脚本生成的目录），用 `sudo chown -R "$USER" 目录` 改回自己。

本机测试是在 bypass 权限模式下跑的，`EACCES` 照样出现。`--dangerously-skip-permissions`、`acceptEdits` 这类模式只跳过 Claude Code 自己的确认，不会给操作系统层面的写权限。它们放行什么、哪些仍会拦，见 [Claude Code --dangerously-skip-permissions：跳过确认、设置与安全替代](https://blog.laozhang.ai/zh/posts/claude-code-dangerously-skip-permissions)。

### macOS 上的 `EPERM`：桌面、文稿、下载文件夹被系统拦截

项目放在 `~/Desktop`、`~/Documents`、`~/Downloads` 或 iCloud 云盘里，看到 `EPERM: operation not permitted`，通常是 macOS 不允许你的终端 App 访问这个文件夹，即使加 `sudo`，在那里运行 `ls` 也会报 `Operation not permitted`。官方报错文档给的处理是：

1. 用 Cmd+Q 完全退出终端 App，重新打开，回到这个文件夹再运行 `claude`。
2. 在那里运行 `ls` 仍然失败，就打开"系统设置 > 隐私与安全性 > 文件和文件夹"，为你的终端 App 打开对应文件夹的开关，再重开终端。

官方文档是在讲读取工作目录失败时给出这条说明的，写入被同一个系统权限拦住时处理相同。更省事的做法是把项目放到 `~/projects` 这类不受隐私保护的目录。

## `InputValidationError` 参数缺失：输出被截断，先写骨架再分块

这条原文的意思是：Claude 发出的 Write 调用里，文件路径或文件内容这两个必填参数缺失了。

```text
Error: InputValidationError: Write failed due to the following issues:
The required parameter `file_path` is missing
The required parameter `content` is missing
```

问题不在磁盘，而在这次回复本身：Claude 在生成 Write 参数的途中被截断，或者参数在传输中丢了。GitHub issue [#895](https://github.com/anthropics/claude-code/issues/895) 至今仍是 open 状态，评论里的触发条件集中在一次写很大的文件、会话很长、多次压缩上下文之后；[#47189](https://github.com/anthropics/claude-code/issues/47189) 是子代理写文件时的同类报告。

和它相关的另一条官方报错是输出超过上限：

```text
API Error: Claude's response exceeded the ... output token maximum. To configure this behavior, set the CLAUDE_CODE_MAX_OUTPUT_TOKENS environment variable.
```

[官方报错文档的输出上限一节](https://code.claude.com/docs/en/errors#response-exceeded-the-output-token-maximum)说明：流式请求失败后，Claude Code 会改用非流式重发，这时回复上限是 64,000 个输出 token，`CLAUDE_CODE_MAX_OUTPUT_TOKENS` 也提不高；v2.1.299 之前，这种重发被截断时本轮会直接结束、没有任何提示。[#7927](https://github.com/anthropics/claude-code/issues/7927) 里有一份旧版本的对话记录，生成大测试脚本时先出现 32000 token 上限报错，紧接着就是 `Error writing file`。所以版本低于 2.1.299 时，先升级，至少能看到明确的截断提示。

处理办法：

1. **同一原文出现第二次就按 Esc 打断。** 重试时 Claude 还会生成同样长的内容，多半在同一位置再被截断，只会继续消耗 token。
2. **改成先写骨架、再分块补。** 官方建议把输出拆小，例如每次回复只写一个文件或一节；中文社区普遍的经验是每次补 100 到 200 行。可以直接这样要求：

```text
先用 Write 创建 docs/design.md，只写标题和各节小标题。
然后用 Edit 逐节补内容，每次只补一节，不超过 150 行。
```

3. **写进 CLAUDE.md**，让以后的会话默认分块写：

```markdown
## 写文件
- 新文件预计超过 200 行时，先写骨架，再分节补充，每次一节。
```

4. 原文提示设置 `CLAUDE_CODE_MAX_OUTPUT_TOKENS` 时，可以按 [Claude Code 环境变量文档](https://code.claude.com/docs/en/env-vars)调高；原文里带 `a request sent without streaming` 时，环境变量无效，只能拆小输出。

### 是不是中转导致的：网关和第三方模型下参数更容易丢

有可能，但这只是用户的分析，Anthropic 没有确认。#895 里一位用户通过 LiteLLM 代理使用时追踪到这样一条链：流式响应出错（扩展日志里是 `Error streaming, falling back to non-streaming mode: Content block not found`），Claude Code 改用非流式重发，非流式的输出上限更低，Write 调用的 JSON 在参数中间被截断，于是报 `content is missing`；重试又走同一条路，形成固定的循环。同一个 issue 里另一位用户反馈，GLM 从 4.5 升级到 4.7 后，所有工具调用到达时参数都是空的。

判断方法：同一个项目、同样的要求，换成 Anthropic 官方接口（或换一个模型）各试一次。官方接口能写、经中转或第三方模型就丢参数，问题就在网关或模型对工具调用的处理上。这时分块写入仍是有效的绕法，长期则要换一条能完整支持流式工具调用的路线。网关、模型的配置和核验方法见 [Claude Code API 配置：先选路由，再设置 Key、模型和网关](https://blog.laozhang.ai/zh/posts/claude-code-api-configuration) 与 [Claude Code 连接 OpenRouter 和 DeepSeek：路线选择、配置与首错排查](https://blog.laozhang.ai/zh/posts/claude-code-openrouter-deepseek)。

## `Error editing file`：`old_string` 对不上、不唯一，或文件是 GBK 编码

Edit 做的是精确字符串替换，不支持正则和模糊匹配。按工具参考文档，Edit 失败基本是以下几种情况：

- **`old_string` 和文件内容不完全一致**：差一个空格或一层缩进就匹配不上，比如文件用 Tab 缩进、Claude 给的是空格。本机复现的原文是 `String to replace not found in file.`，后面跟着它没找到的那段文字。
- **`old_string` 不唯一**：同一段文字在文件里出现多次。本机原文是 `Found 2 matches of the string to replace, but replace_all is false.`，接着提示设 `replace_all` 为 true 全部替换，或者提供更多上下文只改其中一处。
- **没有读过文件**：Opus 4.6、Haiku 4.5 及更早的模型必须先读取才能编辑；被 `PARTIAL view` 提示截断的读取不算数。
- **读取后文件被改**：v2.1.208 起，只要 `old_string` 和当前内容精确、唯一地匹配，仍可应用；否则 Claude 会重新读取。
- **worktree 隔离或软链接**：被拦下的编辑在对话视图里同样只显示 `Error editing file`，Claude 一般会自己换成真实路径重试。

[#8750](https://github.com/anthropics/claude-code/issues/8750) 里出现过 Update 连续失败多次的记录，评论中有用户认为原因是那个文件在 `.gitignore` 里，可以顺手排查，但这只是个别反馈。

### GBK 编码的文件：Edit 拒绝修改，Write 覆盖后变成 UTF-8

中文项目里，老的 C/C++、Java 或批处理脚本常常还是 GBK 编码。从 v2.1.296 起，Edit 碰到这类文件会拒绝修改，文件保持原样，原文开头是：

```text
File is not valid UTF-8. It may use a legacy encoding such as Windows-1252, Shift-JIS or GBK, or be binary
```

拒绝的原因是 Edit 会把整个文件按 UTF-8 存回去，无法解码的字节都会变成替换字符 `U+FFFD`。这条报错会提示 Claude 改用能保留原编码的 shell 命令，或者先问你要不要转成 UTF-8。

这个拒绝是新加的。按[官方报错文档的 File is not valid UTF-8 一节](https://code.claude.com/docs/en/errors#file-is-not-valid-utf-8)，v2.1.296 之前，Edit 和 NotebookEdit 不会报错，而是照常改，把解码不了的字节都存成 `U+FFFD`，GBK 文件里的中文会悄悄变成乱码，界面上也不会出现任何红字。`claude --version` 低于 2.1.296 的，先升级 Claude Code 再让它改旧编码文件，已经改过的文件用 `git diff` 查一下有没有成片的替换字符。

要当心的是 Write：它仍然可以覆盖这种文件（只要新内容里没有 `U+FFFD`），并以 UTF-8 保存，原来的编码就丢了。如果 Edit 被拒后 Claude 转而用 Write 整个重写，GBK 项目里的这个文件可能在编译器或旧系统里变成乱码。

先确认编码：

```bash
file -I path/to/file
```

macOS 用 `-I`，Linux 用 `-i`。结果里的 charset 不是 `utf-8` 或 `us-ascii` 时，按项目情况二选一：

- 项目可以统一成 UTF-8：自己先转换，确认能编译、能运行后再交给 Claude，例如 `iconv -f GBK -t UTF-8 old.c > old.utf8.c`，核对无误再替换原文件。
- 必须保留 GBK：明确告诉 Claude"这个文件是 GBK 编码，不要用 Write 覆盖，用保留编码的方式修改"，改完用上面的 `file` 命令再查一次。

## Write 前没读过文件：Opus 4.6、Haiku 4.5 及更早模型会失败

覆盖已有文件前要不要先读取，取决于模型和版本。[工具参考文档的 Write 一节](https://code.claude.com/docs/en/tools-reference#write-tool-behavior)写的是：

- Opus 4.6、Haiku 4.5 及更早的模型，覆盖本会话没读过的已有文件一定失败。
- 更新的模型在 v2.1.228 及以上，只要读取这个文件不需要权限确认、Read 工具可用，就可以直接覆盖没读过的文件。v2.1.228 之前所有模型都要求先读。
- Jupyter notebook，以及只读到一部分、带 `PARTIAL view` 提示的文件，所有模型都必须完整读取。
- 新建文件不受这条限制。

本机测试 5 用 Opus 5.5 覆盖一个本会话从未读过的已有文件，直接成功。测试 6 只读了一个 162 KB 文件的前 3 行就整文件覆盖，也成功了：用 `limit` 主动只读几行不算 `PARTIAL view`，那个提示只在整个文件超过单次读取的 token 上限、被自动截断时出现。

这一类失败的报错原文在公开文档里没有给出。用 Opus 4.6 或更早的模型、或经网关使用其他模型时，最稳的做法是让 Claude 先完整读取再覆盖。用 `cat`、`head`、`tail`、`sed -n`、`grep`、`rg` 对单个文件查看（不带管道和重定向）也算读过。

## 路径和磁盘：目录、软链接、worktree 拦截与空间不足

- **路径是目录**：本机原文是 `…/isdir is a directory, not a file. To create a file inside it, include the file name in file_path.`，路径里补上文件名即可。
- **父目录不存在**：不是原因。本机测试 7 往一个不存在的多级目录里写文件，Write 自动创建了父目录。
- **软链接**：原文是 `Refusing to write … it is a symbolic link. Write to the link's target path instead`，让 Claude 写链接指向的真实路径。
- **worktree 隔离会话**（v2.1.217 起）：路径经过无法解析的软链接、写成网络共享（UNC 或 `/net`）的形式时，原文以 `This write was blocked because the path is…` 开头，并告诉 Claude 该用哪种路径。通常不用处理，Claude 会按提示重试；同一个文件反复被拦，多半是提交进仓库的软链接目标里带 `..`，让 Claude 直接编辑目标文件。
- **磁盘或配额已满**：原文带 `ENOSPC` 或 `EDQUOT`，指的是 Claude Code 临时目录所在的文件系统。清理空间，或用 `CLAUDE_CODE_TMPDIR` 把临时目录换到其他磁盘后重启 Claude Code。

## 文件其实写好了：相对路径、plan 模式和 Windows 的已知问题

下面几种情况，红字出现了，但文件可能已经正确写入，或者重试即可成功。它们都是 GitHub 上的报告，本机没有复现；这些 issue 都已关闭（多数因长期无回复），没有记录修复版本。截至 2026 年 10 月 16 日，npm 上的最新版本是 2.1.301（10 月 15 日发布），先升级再看是否还出现。

- **相对路径误报**：[#29841](https://github.com/anthropics/claude-code/issues/29841)，v2.1.63，macOS。Write 用相对路径（如 `app/Mcp/Tools/GetMyTasksTool.php`）时每个文件都显示红色 `Error writing file`，文件实际已正确创建。
- **plan 模式首次写入失败**：[#24151](https://github.com/anthropics/claude-code/issues/24151)，最早在 2.1.37（WSL2）上报告，macOS、Ubuntu 2.1.42、Manjaro 2.1.50 都有人确认，最后一个正常的版本是 2.1.29。每次写 plan 文件第一次都失败，紧接着自动重试成功，不影响结果。同一 issue 的评论还提到：确认编辑时选"Yes"再按 Tab 补充提问，Claude 回应后也会显示 `Error writing file`（#31998）。
- **Windows Git Bash 下内容含特殊字符**：[#63838](https://github.com/anthropics/claude-code/issues/63838)，Windows 10，内容里有单引号、反斜杠或 HTML/XML 结束标签时，Write 和 Edit 反复报 `Error writing file` / `Error editing file`，Claude 最后改用 `python -c` 或 heredoc 写文件。
- **Windows MINGW64 下 cygpath 失败**：[#25971](https://github.com/anthropics/claude-code/issues/25971)，每次 Write 都失败，原文是 `Command failed: cygpath -u 'C:\Users\HIPOLI~1\AppData\Local\Temp'`，路径里是 Windows 的 8.3 短文件名。这个 issue 被标为重复后关闭，没有给出修复说明。升级后仍复现，带上原文、版本号和 shell 环境提交新 issue。

如果整个 Claude Code 进程退出，而不是某一次工具调用失败，那是另一类问题，见 [Claude Code process exited with code 1 怎么修：先分支定位再重装](https://blog.laozhang.ai/zh/posts/error-claude-code-process-exited-with-code-1)。

## 写没写进去：用 git diff、wc 和 tail 核对文件

红字之后，不管 Claude 说成功还是失败，都花半分钟核对一次：

```bash
git status --short
git diff --stat
wc -l path/to/file
tail -n 20 path/to/file
```

- 文件在 git 里：`git diff --stat` 看改动行数是否合理，再打开 diff 确认改的是该改的地方。
- 新文件或不在 git 里：`wc -l` 看行数，`tail` 看结尾是否完整。输出被截断时，问题通常出在文件末尾，比如函数没闭合、Markdown 停在半句。
- Claude 改用 Bash、`python -c` 或 heredoc 写的文件：重点看单引号、反斜杠有没有被转义坏，GBK 文件再跑一次 `file -I`。

以下情况停止让 Claude 重试：

- 同一原文第二次出现，而 Claude 还在用同样的方式（同样的整文件 Write）。
- 原文是 `EACCES` 或 `EPERM`。重试改变不了系统权限，先按上文修。
- 核对后文件内容是对的。告诉 Claude"文件已经写好，内容已核对"，让它继续下一步。
- 经中转或第三方模型时 `InputValidationError` 反复出现。先改成分块写，再按上文的方法判断是不是路线问题。

## 本机复现：Claude Code 2.1.295 上的 9 个 Write 与 Edit 场景

测试环境：2026 年 10 月 16 日，Claude Code 2.1.295 桌面版，macOS，Opus 5.5，bypass 权限模式。表中是 Claude 收到的工具结果。

| 场景 | 结果 | 返回的原文或现象 |
| --- | --- | --- |
| 1. 在权限 555 的目录里新建文件 | 失败 | `EACCES: permission denied`，路径是同目录下的 `.tmp.` 临时文件 |
| 2. 文件 `chmod 444`、目录可写，读取后覆盖 | 成功 | 内容被替换，权限仍是 `-r--r--r--` |
| 3. 写入路径是一个目录 | 失败 | `is a directory, not a file` |
| 4. 读取后用 Bash 改动文件，再覆盖 | 失败 | `File has been modified since read…` |
| 5. 覆盖本会话从未读过的已有文件 | 成功 | 无报错 |
| 6. 162 KB 文件只读前 3 行，再整文件覆盖 | 成功 | 无报错，没有触发 `PARTIAL view` 规则 |
| 7. 写入路径的父目录不存在 | 成功 | 自动创建父目录 |
| 8. Edit 的 `old_string` 在文件里不存在 | 失败 | `String to replace not found in file.` |
| 9. Edit 的 `old_string` 出现两次，未设 `replace_all` | 失败 | `Found 2 matches of the string to replace, but replace_all is false.` |

没有在本机跑的：Windows 和 Git Bash 分支、输出 token 截断、相对路径误报和 plan 模式的旧版本 bug、IDE 改变修改时间导致的误报。2.1.301 也没有测，结果可能随版本变化。

## 常见问题

### Claude Code 一直报 Error writing file，是中转导致的吗？

要看原文。原文是 `InputValidationError … content is missing` 时，中转、OpenAI 兼容网关或第三方模型确实是常见条件：GitHub #895 里有用户分析出流式失败后改走非流式、输出上限更低导致参数被截断，但 Anthropic 没有确认。原文是 `modified since read`、`EACCES`、`EPERM` 或编码报错时，和中转无关。

### 让 Claude 分段写入为什么有效？

`content is missing` 一类报错的根源是单次回复太长、在 Write 参数中途被截断。先写骨架、每次只补一节，每次调用的内容都远低于输出上限，就不会被截。它只对截断有效，对权限、读后被改、编码问题没用。

### 我把文件设成只读了，为什么 Claude Code 还是改了它？

Write 先在同一目录写临时文件，再替换目标文件，只要目录可写，文件本身的只读属性挡不住。本机测试里 `chmod 444` 的文件被成功覆盖。要保护文件，在 `.claude/settings.json` 里加 `Edit(...)` 或 `Read(...)` 的 deny 规则，或者让目录不可写。

### Error writing file 会把原来的文件写坏吗？

从 `EACCES` 报错里的临时文件名看，Write 是先写临时文件再替换，写临时文件失败时原文件不会被动到。但已知的误报 bug 里，红字出现时文件其实已经写入了，所以每次都要按上文的命令核对实际内容，不要凭红字判断。

## 参考来源

本文引用的外部页面，按正文出现顺序排列。最后更新于 2026-10-16。

- [Claude Code 报错文档](https://code.claude.com/docs/en/errors) (code.claude.com)
- [Claude Code 交互模式文档](https://code.claude.com/docs/en/interactive-mode) (code.claude.com)
- [#10498](https://github.com/anthropics/claude-code/issues/10498) (github.com)
- [#10245](https://github.com/anthropics/claude-code/issues/10245) (github.com)
- [#11463](https://github.com/anthropics/claude-code/issues/11463) (github.com)
- [Claude Code 工具参考文档](https://code.claude.com/docs/en/tools-reference) (code.claude.com)
- [Claude Code 权限文档的 Read 和 Edit 规则](https://code.claude.com/docs/en/permissions) (code.claude.com)
- [#895](https://github.com/anthropics/claude-code/issues/895) (github.com)
- [#47189](https://github.com/anthropics/claude-code/issues/47189) (github.com)
- [#7927](https://github.com/anthropics/claude-code/issues/7927) (github.com)
- [Claude Code 环境变量文档](https://code.claude.com/docs/en/env-vars) (code.claude.com)
- [#8750](https://github.com/anthropics/claude-code/issues/8750) (github.com)
- [#29841](https://github.com/anthropics/claude-code/issues/29841) (github.com)
- [#24151](https://github.com/anthropics/claude-code/issues/24151) (github.com)
- [#63838](https://github.com/anthropics/claude-code/issues/63838) (github.com)
- [#25971](https://github.com/anthropics/claude-code/issues/25971) (github.com)
