Renamed project root: session registry never revalidates stored cwd — new sessions inherit the dead path, project-scoped settings/hooks/memory all silently fail to load, and the harness recreates the deleted directory
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?
After renaming a project's root directory on disk (mv /Users/me/old-name /Users/me/new-name), several related behaviors combine into a real footgun:
(1) The per-session shell snaps back to the dead path after every Bash call ("Shell cwd was reset to /Users/me/old-name"). Expected for sessions that were already running — but:
(2) Sessions launched after the rename inherit the dead path too, because the stored project entry/session record (~/.claude/sessions/<pid>.json, cwd field) is set from the old registry value at launch and is never revalidated against the filesystem. Verified: a session launched 75 minutes after the rename was born with the dead cwd.
(3) Project-scoped .claude/settings.json (hooks and permissions) silently fails to load entirely for a session rooted at the dead path — confirmed live: a session whose registered root was the dead pre-rename path had no .claude/settings.json there at all, while the correctly-configured file (including a PreToolUse hook) lived only at the real, renamed root. Two real gated tool calls (a git push, an Agent call) both went through with zero interception — not a hook-dispatch bug, the hook was simply never registered. Re-tested from a session genuinely rooted at the correct path: both calls were correctly intercepted. This is a silent, total loss of project-scoped hooks/permissions enforcement, with no error or warning.
(4) Project-scoped persistent memory is orphaned too. Claude Code keys project memory by project root (~/.claude/projects/<munged-project-root>/memory/). After the rename, sessions rooted at the new path get a fresh, empty memory directory — the entire accumulated memory store stays keyed to the old path's entry and is silently invisible to correctly-rooted sessions. Verified: a post-rename session rooted at the new path started with zero recalled memories while ~85 memory files sat under the old entry.
(5) The harness silently recreates the deleted directory tree at the old path (observed: <old-path>/.claude/scheduled_tasks.lock and a duplicate of a repo file written there by a background mechanism), so the dead path resurrects itself with no warning. Deleting the stub is futile while any old-path session lives — the lock file is actively held.
No data loss — live processes' OS-level cwd resolves correctly through the rename (verified via lsof), and the real repo is untouched. But the combination cost real debugging time across multiple sessions, and nothing ever surfaces "your project path no longer exists / was recreated" or "project-scoped config/memory for this session did not load."
What Should Happen?
Any one of: the project registry revalidates its stored path at session launch (or follows the inode/offers to update when the directory is gone); a visible warning that the configured project directory no longer exists, or that project-scoped settings/hooks/memory could not be found at the registered root and the session is running without them; or at minimum, no silent recreation of the deleted path. Renaming/moving a project should migrate (or at least surface) all state keyed to the old root — settings, hooks, and memory alike — rather than silently falling back to an unconfigured/memoryless state with no indication anything is missing.
Error Messages/Logs
Steps to Reproduce
(1) Open a Claude Code session in project /tmp/demo-a; leave it running.
(2) mv /tmp/demo-a /tmp/demo-b
(3) In the running session, run any Bash command → observe "Shell cwd was reset to /tmp/demo-a".
(4) Open a new session from the same project entry in the app → its Bash cwd is also /tmp/demo-a (dead), and ~/.claude/sessions/<pid>.json records "cwd": "/tmp/demo-a".
(5) That new session has no project-scoped .claude/settings.json in effect (any hooks/permissions configured at the real, renamed root are silently not loaded) and no access to project memory accumulated at the real root.
(6) Observe /tmp/demo-a reappear on disk containing .claude/scheduled_tasks.lock.
Claude Model
None
Is this a regression?
No, this never worked
Last Working Version
_No response_
Claude Code Version
2.1.219 (desktop app)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Related issues: #54175 (docs: moved launch-directory behavior undocumented), #55587 (preview_start after project rename, closed as duplicate), #18236 (Bash after deleted working directory). This report is specifically about the persistent registry + silent recreation + silent project-scoped settings/hooks/memory loss for post-rename new sessions, which none of those cover.
3 Comments
Same root cause, with a minimal reproduction procedure that does not require reinstall or any special setup:
A new project folder is created for the new path; the original transcript stays in
~/.claude/projects/<old-encoded-path>/, intact but unreferenced. Nothing reports that anything is missing.Verified on CLI 2.1.243, Windows. The folder-naming scheme is not platform-specific, so I would expect the same issue unix like OSes (not tested there).
Worth noting this is just one case among several for the same design — the folder name below
~/.claude/projects/is derived from the full project/working pathname and is the sole lookup key, so any variation in that string detaches history:not_plannedby automation, not sure if it was triaged.\\host\sharevs//host/share), so one directory ends up with two project folders. The stale one takes precedence on restart, history appears to disappear, and the reporter says one transcript was lost for good.Addressing these one trigger at a time leaves the next one open. A stable per-project identity would cover all of them; failing that, detecting that transcripts exist under a name that no longer resolves — and reporting it — would turn a silent failure into an easily solvable one.
The transcript is likely still present under the old project slug. Renaming the folder changes the path key used for project-scoped sessions, so new sessions resolve the renamed folder while earlier records remain attached to the old
cwd. That also explains why the old hooks, permissions, and project memory stop appearing without implying that the conversation data was deleted.I built
BasedGPT/claude-code-session-recoveryfor this file-layer split. From that checkout, runpython tools/diagnose.pyfirst and follow the exact command it prints. It compares the storedcwdand project slug with the transcript locations, distinguishing one project split across two session sets from a genuinely missing JSONL file before any metadata rewrite.Before starting another
--continuein the renamed folder, compare the old and new~/.claude/projects/<slug>/directories and inspect thecwdrecord in one affected transcript. Keep a byte-for-byte copy of the old directory. If the diagnosis identifies a supported path repair, apply it only after checking that the transcript count and hashes are unchanged; otherwise leave the files in place rather than guessing at a new project key.Hope this helps, if my tools are able to help you, would appreciate a ⭐ :)
Thanks for your feedback. Actually, I already solved my problem. In my case, my main computer died and I took the drive from it to another, mounting it with a different drive letter. I wanted to restore my profile exactly as it was on the old computer and I hit the documented issue. With Claude's help, I succeeded and along the way, like you, I ended up developing a tool to automate the recovery process. The result is at https://github.com/josealf/claude-profile-migrate. After that, I wanted to report the problem upstream, but I found this and the other related threads.