[BUG] Claude refuses to write CLAUDE.md when it's a symlink
Status Open
Reported on v2.1.168
Maintainer reply None cached
Workaround ✓ Mentioned in thread ↓
Activity 6 comments · opened Jun 9, 2026
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
As per the official documentation having CLAUDE.md as a symlink should be supported, however the internal Update an Write tools refuse to change it directly.
What Should Happen?
Allow writing to CLAUDE.md directly when it's a symlink of adjacent AGENTS.md or change advice away from using a symlink.
Error Messages/Logs
● Update(/tmp/symlink-repro/CLAUDE.md)
⎿ Error: Refusing to write through symlink: /tmp/symlink-repro/CLAUDE.md. Resolve the symlink and pass
the real target path explicitly.
● Write(/tmp/symlink-repro/CLAUDE.md)
⎿ Error: Refusing to write through symlink: /tmp/symlink-repro/CLAUDE.md. Resolve the symlink and pass
the real target path explicitly.
Steps to Reproduce
mkdir /tmp/symlink-repro && cd /tmp/symlink-repro
printf 'real content\n' > AGENTS.md
ln -s AGENTS.md CLAUDE.md
Then, in Claude Code:
ReadCLAUDE.md— succeeds.EditCLAUDE.md(any change) — fails with:
Refusing to write through symlink: /tmp/symlink-repro/CLAUDE.md. Resolve the symlink and pass the real target path explicitly.
WritetoCLAUDE.md— fails with the same message.ReadthenEditAGENTS.md(the real target) — succeeds, and the
symlink is left intact and reflects the change when read back through
CLAUDE.md.
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.168
Platform
AWS Bedrock
Operating System
Ubuntu/Debian Linux
Terminal/Shell
VS Code integrated terminal
Additional Information
_No response_
Showing cached comments. Read the full discussion on GitHub ↗
4 Comments
Found 2 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
I’m hitting the same class of issue, but not with
CLAUDE.mddirectly.Scenario:
.claude/directory is a symlink to a shared.agents/location..claude/settings.json.Error:
Reproduced on
2.1.172(a newer build than your2.1.168), so this is still live, not fixed since you filed:Read CLAUDE.md→ succeeds (returns the real content through the link)Edit/Write CLAUDE.md→Refusing to write through symlink: …/CLAUDE.md. Resolve the symlink and pass the real target path explicitly.Read+Edit AGENTS.md(the real target) → succeeds, link stays intact, and the change reads back correctly throughCLAUDE.md.So the
SymlinkWriteRefusedErroris an intentional anti-symlink-attack guard (a tool shouldn't be tricked into writing through an arbitrary link), but it doesn't special-case the documented self-referentialCLAUDE.md → AGENTS.mdsetup. That's the real contradiction you're pointing at: the memory docs say "a symlink also works," yet the moment Claude itself needs to update the file (auto-memory,/initimprovements, or you asking it to edit CLAUDE.md) the write is refused.Two practical workarounds today:
AGENTS.mddirectly. The symlink reflects the change and stays intact (verified above). This is the minimal fix if you want to keep the symlink.CLAUDE.mda real file whose only content is an import:``
markdown
``@AGENTS.md
The docs present the symlink as the secondary option ("A symlink also works if you don't need to add Claude-specific content"), with
@AGENTS.mdas the first-class one. BecauseCLAUDE.mdis now a regular file, Edit/Write work normally, and you can append Claude-only sections below the import:```markdown
@AGENTS.md
## Claude Code
Use plan mode for changes under
src/billing/.```
One more reason to prefer the import over a symlink — cross-platform robustness. The docs already note that creating a symlink on Windows needs Admin/Developer Mode. There's a second, quieter failure on the clone side: Git for Windows defaults to
core.symlinks=false, and under that setting a committed symlink is checked out as a tiny text file, not a link. Verified:So a teammate on default Git-for-Windows ends up with a
CLAUDE.mdwhose entire content is the literal 9 bytesAGENTS.md— every instruction silently gone, no error. The@AGENTS.mdimport is a normal file and clones identically everywhere, so it's strictly safer for shared repos.For maintainers: either special-case the self-referential
CLAUDE.md ↔ AGENTS.mdsymlink in the write guard, or change the docs to lead with@AGENTS.mdand demote theln -sexample (it can't be edited by the tools that are supposed to maintain it).@Damecek's case is the same guard class but for a symlinked directory (
.claude/→ shared.agents/…), wheresettings.jsonwrites hitSymlinkWriteRefusedError: Refusing to write into symlinked directory. The@importtrick doesn't help there since it's a directory, not a memory file — that's worth a separate report, because the resolution (allow writes into a user-created symlinked config dir) is different from the memory-file case here.I have the same problem with .claude dir symlinked to .agents. It cannot write settings.local.json file here