Claude Code “Error editing file”: Press Ctrl+O to See the Real Reason (5 Causes Reproduced)
A short "Error editing file" line hides the real reason. Press Ctrl+O to open the transcript, then match the full message. We reproduced five refusals on Claude Code 2.1.312 against a local mock API and quote each message.

Short answer: the Error editing file line in Claude Code is only a label. The real reason is in the full tool error, which you open with Ctrl+O (the transcript view). On November 10, 2026 we reproduced five common refusals on Claude Code 2.1.312 against a local mock API (no login, nothing sent to Anthropic): a file that is not UTF-8, a Read deny rule, a symlink, a file Claude had not read yet, and a string that no longer matches. The full messages are quoted below.
Step 1: press Ctrl+O to see the full error
The official Error reference (fetched Nov 10, 2026; it cites versions up to v2.1.318) says, in its entry for paths that cannot be safely resolved, that for a blocked file edit the conversation view shows only a short Error editing file line and the full message is in the transcript view, opened with Ctrl+O. The same message also goes to Claude as a tool error, so Claude often retries a different way on its own.
We could not capture that short line in the interactive UI: our tests used claude -p (non-interactive), which prints the full tool result directly. All quotes below came from that.
Five causes at a glance
Get the full line with Ctrl+O, then match its start:
| Message starts with | What it means | What to do | Source |
|---|---|---|---|
File is not valid UTF-8: it uses a legacy encoding such as Windows-1252, Shift-JIS or GBK, or it is binary. | The file is not UTF-8 (old GBK files are the classic case); nothing was written | Convert to UTF-8, or have Claude edit it with a shell command in its own encoding | Docs + reproduced on 2.1.312 |
File is covered by a Read deny rule in your permission settings and cannot be edited. | A permissions.deny Read rule matches the path | Narrow or remove that Read deny rule | Docs + reproduced on 2.1.312 |
Refusing to write /path/CLAUDE.md: it is a symbolic link. Write to the link's target path instead: /path/AGENTS.md. | The write location itself is a symlink | Edit the real target file | Docs + reproduced on 2.1.312 |
File has not been read yet. Read it first before writing to it. | Claude has not read this file in this session | Have Claude read it, then edit | Reproduced on 2.1.312 (not found in the docs) |
String to replace not found in file. | The old text Claude supplied does not match the file; it may have changed | Have Claude re-read the file, then edit | Reproduced on 2.1.312 (not found in the docs) |
How we reproduced it
Setup: Claude Code 2.1.312 (claude --version) with ANTHROPIC_BASE_URL pointing at a small Python server on the same machine. It pretends to be the Messages API and just emits a scripted Read and Edit tool call. No real model, no login, no request to Anthropic. We ran claude -p with --permission-mode acceptEdits.
1. Not UTF-8. Create a GBK file:
printf '你好,世界\n版本=1\n' | iconv -f UTF-8 -t GBK > gbk.txt
The Read result ends with a note: [This file is not valid UTF-8: it uses a legacy encoding such as Windows-1252, Shift-JIS or GBK, or it is binary. ...]. The Edit is then refused. Full text (outer tool_use_error tags removed):
File is not valid UTF-8: it uses a legacy encoding such as Windows-1252, Shift-JIS or GBK, or it is binary. These tools write UTF-8 (or UTF-16LE behind its byte-order mark) and never convert a file's encoding on their own, so nothing was written. If the file is text, make the change with a shell command that reads and writes it in its own encoding (iconv, or PowerShell Get-Content/Set-Content -Encoding), or ask the user whether to convert it to UTF-8 first. To replace the whole file with new UTF-8 text anyway, delete it first, then Write it.
The docs word this slightly differently (It may use a legacy encoding ... Nothing was written.) from what 2.1.312 printed, so trust your own screen; the key phrase File is not valid UTF-8 is the same. The docs also say that before v2.1.296 Edit saved undecodable bytes as U+FFFD, which silently damages the file, so update old versions first.
2. Read deny rule. With {"permissions":{"deny":["Read(./secret.txt)"]}} in the project’s .claude/settings.json, an edit of secret.txt returned:
File is covered by a Read deny rule in your permission settings and cannot be edited.
3. Symlink. With CLAUDE.md created by ln -s AGENTS.md CLAUDE.md, reading and then editing CLAUDE.md returned:
Refusing to write /tmp/cct/proj/CLAUDE.md: it is a symbolic link. Write to the link's target path instead: /tmp/cct/proj/AGENTS.md.
4. Not read yet / string not found. Editing a file without reading it first gave File has not been read yet. Read it first before writing to it. After reading, replacing text that is not in the file gave String to replace not found in file. followed by the string.
Two items we did not reproduce (docs only, do not treat them as verified by us):
- Paths that cannot be safely resolved (worktree isolation, a symlink target containing
..): this needs a worktree session, which we did not run. permissions.blockWritesOutsideWorkingDirectories: with it set in the project settings on 2.1.312, a write outside the directory underbypassPermissionsstill succeeded. Official docs say this setting requires Claude Code v2.1.314 or later. We did not try a newer version or user-level settings, so we cannot say the setting is broken, only that we did not reproduce the block. See /permissions and blockWritesOutsideWorkingDirectories.
Fixes by cause
1. Not UTF-8
- To keep using Edit: back up, then convert. GBK to UTF-8:
cp gbk.txt gbk.txt.bak
iconv -f GBK -t UTF-8 gbk.txt.bak > gbk.txt
- First check whether other tools (old build scripts, legacy Windows programs) still expect GBK. If so, do not convert; let Claude use a shell command in the original encoding, as the message says.
- The docs add that a file meant to be UTF-8 is refused too if it contains even one invalid byte sequence.
2. Read deny rule
- If Claude should change the file: narrow or remove the Read deny rule in
/permissionsor settings. - If the file must stay untouched: keep the rule and add an Edit deny rule for the same path, which also blocks NotebookEdit (the docs’ advice).
3. Symlink
Edit the real target the message names. The docs give the same advice for committed symlinks whose target contains .., such as docs/current -> ../README.md.
4. Not read yet, string not found
Both mean Claude’s picture of the file differs from what is on disk. Have it re-read the file first. If it repeats on the same file, check for a formatter, a sync tool or another session editing it at the same time.
5. Blocked paths, writes outside the project
Follow the official docs: Claude usually retries with the real path the message gives; for a blocked write outside the project use /add-dir, or remove the setting. We did not reproduce these; see the Tool errors section of the Error reference.
FAQ
Why do I see a different message in -p mode or scripts?
-p has no interactive UI, so the full tool error is in the output. We also saw Claude requested permissions to write to /tmp/cct/outside.txt, but you haven't granted it yet.: an unapproved write outside the project fails like this. It is not a file problem; think about what you are opening up before adding --permission-mode or --allowedTools.
Will updating Claude Code fix it?
Only for the old behavior where non-UTF-8 files were silently damaged (refused from v2.1.296). The others are configuration or file issues that an update will not change.
More Claude Code errors
See the Claude Code error list. For the version 400 see does not support this model; for Git lock files see index.lock File exists.