[BUG] Claude Desktop replaces symlinked claude_desktop_config.json with a regular file on save (atomic write is not symlink-aware)

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 22, 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?

Claude Desktop saves ~/.config/Claude/claude_desktop_config.json with a non-symlink-aware atomic write (write a temp file, then rename() it over the target path). When that path is a symlink — a common dotfiles setup where the config is symlinked into a version-controlled repo — the rename() replaces the symlink itself with a brand-new regular file instead of writing through the link to its target. The symlink is silently destroyed on the first settings change, so the file is no longer linked into the repo and further edits stop syncing.

What Should Happen?

Saving the config should preserve a symlink at that path. A symlink-aware atomic write resolves the real target first (realpath), writes the temp file in the target's directory, and renames onto the resolved target — so the symlink at ~/.config/Claude/claude_desktop_config.json is kept and points at the updated file. (This is exactly what the widely used write-file-atomic npm package does by default.)

Error Messages/Logs

# No error is emitted — the symlink is replaced silently. Evidence via stat before/after a settings change:

# Before (managed symlink):
$ ls -la ~/.config/Claude/claude_desktop_config.json
lrwxrwxrwx ... claude_desktop_config.json -> ../../dotfiles/.../claude_desktop_config.json

# After changing any setting in the app:
$ stat -c 'type=%F inode=%i' ~/.config/Claude/claude_desktop_config.json
type=regular file inode=92166112     # new inode; was 92165154 (the symlink target) — proof of temp-file + rename

Steps to Reproduce

  1. Quit Claude Desktop.
  2. Move the config into another directory and symlink it back, e.g.:

``bash
mv ~/.config/Claude/claude_desktop_config.json ~/some-repo/claude_desktop_config.json
ln -s ~/some-repo/claude_desktop_config.json ~/.config/Claude/claude_desktop_config.json
``

  1. Start Claude Desktop and change any setting (e.g. toggle a preference), so the app saves the config.
  2. Inspect the path: ls -la ~/.config/Claude/claude_desktop_config.json — it is now a regular file, not a symlink, and the repo copy no longer receives updates.

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

Claude Desktop app, version 1.22209.3

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

Other

Additional Information

The atomic-write pattern itself is correct and desirable (it prevents a corrupted/half-written config on crash) — the gap is only that it doesn't resolve symlinks before writing. Note this fix would preserve symlinks specifically; a hardlink cannot survive a rename() regardless. Supporting symlinked config would let users manage claude_desktop_config.json with dotfiles managers (GNU Stow, chezmoi, bare-repo setups, etc.), which is currently impossible because every save breaks the link.

Related issues (same root cause, different files — this appears to be a systemic pattern across config writers, so a shared symlink-aware atomic-write fix would address all of them): #76561 reports the identical mechanism for ~/.claude/.credentials.json ("credential writes replace symlinks with private regular files… rename() … never follows a symlink"); #78162 is a related symlink-aware atomic-write problem for ~/.claude/settings.json (resolves the symlink only one hop instead of full realpath) and proposes the same realpath fix; #67208 (closed) was an earlier settings.json symlink mis-resolution. This report is the Claude Desktop claude_desktop_config.json instance of the same class of bug.

Suggested fixes
  1. Make the config writer symlink-aware. Before the atomic write, resolve the path with fs.realpath (or equivalent); write the temp file in the resolved target's directory (same filesystem, so rename() stays atomic) and rename onto the resolved target. The symlink at the original path is preserved.
  2. If already using write-file-atomic, ensure its default symlink resolution is not disabled (some wrappers pass an option or pre-resolve to the link path, which reintroduces this bug).
  3. At minimum, document the limitation and/or detect when the target is a symlink and preserve it, so dotfiles users aren't silently surprised.

View original on GitHub ↗