CLI resolves symlinks in working directory instead of preserving $PWD logical path
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 Code resolves the working directory to its physical path at startup,
discarding the logical path the user navigated through. When the user reaches
their project via symlinks, every path displayed in output, tool results, and
file references uses the fully dereferenced physical path instead of the
symlink-based path the user actually works with.
For example, my shell's $PWD is:
/home/user/txt/duetto/c/duetto2
But Claude Code resolves this to:
/home/user/Documents/Dropbox/txt/duetto/code/duetto2
All tool output (Read, Glob, Grep, Edit, etc.) then references the long
physical path. The symlink-based path, which is the one present in my shell
history, aliases, scripts, and muscle memory, is never used.
This also degrades relative path computation. The physical path sits 7+
directories below /, so cross-module references produce chains like../../../../../../.. that are unreadable and unusable. The symlink exists
specifically to provide a short anchor point close to /; if Claude
respected it, those same references would be 2-3 levels of ../ at most.
Even when a relative path is technically correct, a short absolute path
through the symlink (e.g., /home/user/txt/duetto/c/duetto2/some/file)
will almost always be preferable to an ungainly string of ../../../..
derived from the deep physical hierarchy. Resolving the symlink away
throws out both benefits: short absolute paths and shallow relative ones.
What Should Happen?
Claude Code should use $PWD (the logical path from the shell environment)
as the working directory rather than resolving it via realpath() orfs.realpathSync(). This is what the user's shell shows, what their prompt
displays, and what all their other tools use.
Most shells maintain $PWD as the logical path (preserving symlinks) by
default (set -o logical). Node.js process.cwd() returns the physical
path on Linux, but process.env.PWD contains the logical path. The fix
would be to prefer process.env.PWD (after validating it points to the
same inode as process.cwd()) for display and tool path construction.
Steps to Reproduce
- Create a symlink to a project directory:
``bash``
ln -s /long/physical/path/to/project ~/short/project
cd ~/short/project
- Confirm the shell preserves the logical path:
``bash``
echo $PWD # /home/user/short/project
pwd # /home/user/short/project
pwd -P # /long/physical/path/to/project
- Launch
claude
- Ask Claude to read or reference any file. All paths in tool output use
the physical path, not the symlink path.
Environment
- Platform: Linux (Ubuntu 22.04)
- Terminal: CLI
- Version: 2.1.39
Related Issues
- #28201 — Same root cause on Windows (junction points resolved instead of
using workspace path). Currently Windows/VS Code scoped.
- #764 — Symlink resolution failure for
~/.claudedirectory. - #16507 — Glob tool does not follow symbolic links.
- #34125, #36610, #33140 —
fs.realpath()resolving mapped drives to UNC
paths on Windows, breaking session matching. Same underlying pattern.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗