[BUG] Claude refuses to write CLAUDE.md when it's a symlink

Status Open
Reported on v2.1.168
Maintainer reply None cached
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:

  1. Read CLAUDE.md — succeeds.
  2. Edit CLAUDE.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.

  1. Write to CLAUDE.mdfails with the same message.
  2. Read then Edit AGENTS.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_

View original on GitHub ↗

4 Comments

github-actions[bot] · 2 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/40857
  2. https://github.com/anthropics/claude-code/issues/58443

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

Damecek · 2 months ago

I’m hitting the same class of issue, but not with CLAUDE.md directly.

Scenario:

  • The project-level .claude/ directory is a symlink to a shared .agents/ location.
  • Installing a Claude Code plugin fails when it tries to update .claude/settings.json.

Error:

Failed to update settings: Failed to read raw settings from /Users/xxx/xxx/.agents/worktrees/unruffled-morse-b67776/.claude/settings.json: SymlinkWriteRefusedError: Refusing to write into symlinked directory:
  /Users/xxx/xxx/.agents/worktrees/unruffled-morse-b67776/.claude
yurukusa · 2 months ago

Reproduced on 2.1.172 (a newer build than your 2.1.168), so this is still live, not fixed since you filed:

mkdir /tmp/symlink-repro && cd /tmp/symlink-repro
printf 'real content\n' > AGENTS.md
ln -s AGENTS.md CLAUDE.md
  • Read CLAUDE.md → succeeds (returns the real content through the link)
  • Edit/Write CLAUDE.mdRefusing 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 through CLAUDE.md.

So the SymlinkWriteRefusedError is 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-referential CLAUDE.md → AGENTS.md setup. 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, /init improvements, or you asking it to edit CLAUDE.md) the write is refused.

Two practical workarounds today:

  1. Edit the real target, not the link. Point Read/Edit/Write at AGENTS.md directly. The symlink reflects the change and stays intact (verified above). This is the minimal fix if you want to keep the symlink.
  1. **Use the import directive instead of a symlink — this sidesteps the bug entirely and is actually the docs' primary recommendation.** Make CLAUDE.md a 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.md as the first-class one. Because CLAUDE.md is 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:

git -c core.symlinks=false clone <repo>
file CLAUDE.md      # ASCII text, with no line terminators
cat  CLAUDE.md      # AGENTS.md
wc -c CLAUDE.md      # 9

So a teammate on default Git-for-Windows ends up with a CLAUDE.md whose entire content is the literal 9 bytes AGENTS.md — every instruction silently gone, no error. The @AGENTS.md import 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.md symlink in the write guard, or change the docs to lead with @AGENTS.md and demote the ln -s example (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/…), where settings.json writes hit SymlinkWriteRefusedError: Refusing to write into symlinked directory. The @import trick 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.

Igorgro · 1 month ago

I have the same problem with .claude dir symlinked to .agents. It cannot write settings.local.json file here

Showing cached comments. Read the full discussion on GitHub ↗