[BUG] Edit tool silently fails (reports success, no write) when target is a relative `..` symlink inside a symlinked-directory chain
Summary
Edit returns "The file ... has been updated successfully." but the target file on disk is unchanged when the path satisfies all of:
- The directory portion traverses one or more symlinked directories, and
- The leaf path component is itself a symbolic link, and
- The leaf symlink's target is a relative path containing
..(e.g.../settings.json).
Removing any one of these conditions → write persists correctly (bisect below). This is a silent correctness bug: no error, no warning, no tmp file left behind, the write goes nowhere. The symlink on disk is not replaced with a regular file (distinguishing this from #28376 / worktree-related #40857).
What Should Happen
Either persist the write through to the resolved real file, or return an error.
Steps to Reproduce
Build the minimal layout in a shell:
R=$(mktemp -d -t claude-symlink-bug)
mkdir -p "$R/real/host"
echo '{"k":"v1"}' > "$R/real/settings.json"
ln -s ../settings.json "$R/real/host/settings.json" # leaf symlink, relative parent target
ln -s real/host "$R/synced" # dir symlink
ln -s synced "$R/dot" # dir symlink
echo "Edit this path: $R/dot/settings.json"
In a Claude Code session:
Read→$R/dot/settings.json. Content:{"k":"v1"}.Editthe same path,old_string="{\"k\":\"v1\"}",new_string="{\"k\":\"v2\"}".- Edit returns: "The file ... has been updated successfully."
- Back in the shell:
````
cat "$R/real/settings.json" # still {"k":"v1"}
ls -la "$R/real/host/settings.json" # still a symlink, not replaced
Bisect
Six configurations tested on v2.1.118 / macOS 26.3 (Darwin 25.3, arm64) / APFS. Only the one matching the real failing layout silently fails:
| # | Dir-symlink chain | Leaf is symlink | Leaf target | Edit persists? |
|---|---|---|---|---|
| 1 | no | yes | sibling in same dir | ✓ |
| 2 | yes | yes | ../foo.json (parent-relative) | ✗ silent success, no write |
| 3 | no | yes | ../foo.json (parent-relative) | ✓ |
| A | yes | no (real file at leaf) | — | ✓ |
| B | yes | yes | sibling in same dir | ✓ |
| C | yes | yes | absolute path | ✓ |
Trigger is specifically relative .. in the leaf symlink target combined with a symlinked-directory chain on the access path. Either alone is safe; the combination is not.
Likely Cause (speculation)
The leaf symlink's relative target appears to be resolved against the virtual (symlink-walked) path rather than against the symlink's actual on-disk containing directory. With the access path $R/dot/settings.json, the leaf's ../settings.json should resolve to $R/real/settings.json (relative to $R/real/host/, where the symlink lives); instead the write seems to target something that doesn't exist or is silently discarded.
Alternatively, the tool may detect the configuration and bail without surfacing an error.
Impact
Silent data loss for shared-config layouts that use symlinked directories with relative-up leaf symlinks — a common pattern for dotfile managers and multi-machine sync setups. Specifically bit a user trying to sync ~/.claude/ across two Macs via iCloud: ~/.claude → .claude-synced → .claude-home/<hostname>/ (two dir symlinks) and settings.json → ../settings.json inside the hostname dir (leaf symlink, relative up) — exactly config #2. Settings edits appeared successful in-session and the sync architecture had to be abandoned after the silent failures were diagnosed by byte-comparing backups against post-Edit state.
Related Issues (not duplicates)
- #28376 (closed/stale) — Write/Edit replaces a symlink with a regular file. Different symptom: there the target file never updates and the symlink is destroyed; here the symlink is preserved and the write lands nowhere observable.
- #40857 —
symlinkDirectoriesworktrees: write replaces symlink with regular file. Similar shape to #28376, worktree-scoped. - #44035 — Claude Code reports successful writes / git commits that never happen. Different root cause (long-running Ralph-Loop session, possibly no tool call emitted); our bug is a single-Edit, 30-second deterministic repro.
- #51214 — Desktop app: Write/Edit silently fails to flush mid-session. Platform differs (Desktop, post-rewrite 1.3109).
- #4462 — Sub-agents claim successful file creation that doesn't persist. Sub-agent scoped.
- #764 — Symlink resolution failure traversing symlinked dirs. Older, about Read/traversal.
Workarounds
- Target the resolved real path directly (
readlink -f), bypassing the symlink chain. - Use
Bashto write (python3 -c "open(p,'w').write(s)",sed -i, etc.) — these follow symlinks normally. - Restructure the layout to avoid relative
..in leaf symlinks: use absolute targets (config C works), or keep the leaf as a real file inside a symlinked directory (config A works).
Environment
- Claude Code version:
2.1.118 - API platform: Anthropic API
- OS: macOS 26.3.1 (Darwin 25.3.0, arm64)
- Terminal: Terminal.app
- Filesystem: APFS
- Regression: unknown — observed on 2.1.116 (prior session) and 2.1.118 (current); earlier versions not tested.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗