[BUG] Internal project path key inconsistency in git worktrees inside devcontainers — sessions and memory stored under different keys, /resume finds nothing
Description
When running Claude Code CLI inside a devcontainer that checks out a git worktree (not created by Claude Code), the internal project path key used for session storage differs from the key used for memory storage and /resume lookups. Sessions are written to one ~/.claude/projects/ subdirectory, while /resume and the memory system look in a different one. Result: /resume reports "No conversations found to resume" despite hundreds of valid session .jsonl files on disk.
This appears to be a path resolution inconsistency — some subsystems derive the project key from cwd, others from git rev-parse --git-common-dir (which follows the worktree pointer to the main repo). When these resolve to different filesystem paths (common in containers where mount points differ from host paths), the keys diverge.
Environment
- Claude Code v2.1.74 (CLI)
- Linux (devcontainer on WSL2)
- Git worktree (external, not created by Claude Code)
Reproduction
Setup
A devcontainer where:
- The workspace is mounted at
/workspaces/myproject - The main repo's
.gitdir is bind-mounted at its host path/home/alice/repos/myproject/.git(required so the worktree.gitpointer resolves correctly inside the container) /workspaces/myproject/.gitis a worktree pointer file containing:
````
gitdir: /home/alice/repos/myproject/.git/worktrees/feature-branch
This is a standard setup for running git worktrees in devcontainers — the main .git dir must be mounted at its original host path because git worktree .git pointer files contain absolute paths written by git worktree add.
Git's own path resolution (inside the container)
$ git rev-parse --show-toplevel
/workspaces/myproject # ← cwd-based (correct)
$ git rev-parse --git-common-dir
/home/alice/repos/myproject/.git # ← main repo (host path)
Git itself resolves --show-toplevel correctly to the container workspace path.
What Claude Code does
| Subsystem | Project key | Derived from | Has data? |
|-----------|------------|-------------|-----------|
| Session writer | ~/.claude/projects/-workspaces-myproject/ | cwd | 199 .jsonl files |
| Memory system | ~/.claude/projects/-home-alice-repos-myproject/ | git-common-dir parent | Only memory/ dir, 0 sessions |
| /resume picker | ~/.claude/projects/-home-alice-repos-myproject/ | (same as memory) | Finds 0 conversations |
Steps
- Create a git worktree on the host, open it in a devcontainer
- Start Claude Code CLI, have a conversation, exit
- Start a new session, type
/resume - Expected: Previous sessions appear in picker
- Actual: "No conversations found to resume"
Evidence
# Sessions exist under the cwd-keyed directory
$ ls ~/.claude/projects/-workspaces-myproject/*.jsonl | wc -l
199
# But the git-resolved directory has no sessions
$ ls ~/.claude/projects/-home-alice-repos-myproject/
memory/ # ← only memory files, zero .jsonl
# The session files are valid and recent
$ ls -lt ~/.claude/projects/-workspaces-myproject/*.jsonl | head -3
-rw------- ... 15:01 f14a6c88-....jsonl # current session
-rw------- ... 14:41 81202388-....jsonl # previous session today
-rw------- ... 14:20 81486591-....jsonl # earlier today
Root cause hypothesis
Claude Code uses at least two different algorithms to compute the project directory key:
cwd-based (used by session writer): Slash-encodes the current working directory →-workspaces-myprojectgit-common-dir-based (used by memory,/resume, permissions per #28248): Follows the worktree.gitpointer to the main repo's.gitdir, derives the parent as the project root →-home-alice-repos-myproject
In a normal (non-worktree) repo, or when the workspace mount path matches the host path, these resolve to the same directory. In a git worktree inside a container where the main repo's .git is mounted at its host path (different from cwd), they diverge.
Note: The devcontainer setup is correct. The main .git dir must be mounted at its original host path because git worktree .git pointer files contain absolute paths written by git worktree add. Git itself handles this correctly — --show-toplevel returns the workspace cwd, not the host path.
Suggested fix
The project directory key should be computed consistently across all subsystems. git rev-parse --show-toplevel is the most correct source — it already handles worktrees correctly and returns the workspace path that matches user expectations, even inside containers with remapped mount points.
Related issues
- #28248 — Permission scoping uses main worktree path instead of current worktree path (confirms
git-common-dircode path exists) - #28019 — Sessions not discoverable across worktrees (session discovery is path-scoped)
- #15776 — Session state lost when worktree deleted
- #19995 —
/resumereports no conversations despite files on disk (may share root cause) - #26123 — General
/resumebugs since v2.1.31 (may be a compounding factor)
Workaround
Symlink the git-resolved project dir to the cwd-keyed one. Add to devcontainer postStartCommand or setup_env.sh:
if command -v git &> /dev/null && [ -f "$WORKSPACE/.git" ]; then
CLAUDE_PROJECTS="$HOME/.claude/projects"
CWD_KEY="${CLAUDE_PROJECTS}/$(echo "$WORKSPACE" | tr '/' '-')"
GIT_COMMON_DIR="$(cd "$WORKSPACE" && git rev-parse --git-common-dir 2>/dev/null)" || true
if [ -n "$GIT_COMMON_DIR" ]; then
GIT_ROOT="$(cd "$GIT_COMMON_DIR/.." && pwd)"
GIT_KEY="${CLAUDE_PROJECTS}/$(echo "$GIT_ROOT" | tr '/' '-')"
if [ "$CWD_KEY" != "$GIT_KEY" ]; then
mkdir -p "$CWD_KEY"
if [ -d "$GIT_KEY" ] && [ ! -L "$GIT_KEY" ]; then
cp -rn "$GIT_KEY"/. "$CWD_KEY"/ 2>/dev/null || true
rm -rf "$GIT_KEY"
fi
[ ! -L "$GIT_KEY" ] && ln -s "$CWD_KEY" "$GIT_KEY"
fi
fi
fi
6 Comments
Update: Three compounding bugs identified (with source analysis)
After reverse-engineering the relevant code paths from the v2.1.74 binary, the issue is worse than originally reported. There are three compounding bugs, not one.
Bug 1: Session writer vs worktree scanner use different path sources
cwd(e.g.,/workspaces/myproject→-workspaces-myproject)/resumeworktree scanner (dP8()) callsgit worktree list --porcelain, which returns host paths (e.g.,/home/alice/repos/myproject). Inside a devcontainer, these host paths don't match the containercwd, so the prefix scan finds zero matching project directories.The relevant code flow:
Bug 2:
isDirectory()skips symlinksIn
dP8(), the directory scanner does:Node.js
readdir({withFileTypes: true})returnsDirentobjects where symlinks haveisDirectory() === falseandisSymbolicLink() === true. A symlink workaround is therefore impossible.Bug 3: Inconsistent project key across subsystems (original report)
As originally reported — the session writer, memory system, and
/resumeeach derive the project directory key differently.Working workaround
Since symlinks are skipped, the only workaround is hardlinking session
.jsonlfiles from the cwd-keyed directory into the git-resolved directory. This creates a real directory with real files that passisDirectory()on the parent and are discoverable by the prefix scan.Confirmed working —
/resumenow finds all 199 sessions after hardlinking.Limitation: Only picks up sessions that existed at container start. New sessions created during a running session won't appear until the next restart.
Suggested fix (updated)
The root fix should address all three:
git rev-parse --show-toplevelinstead ofgit worktree listpaths for the current worktree's project key —--show-toplevelcorrectly returns the containercwdeven inside worktreesisDirectory()ORisSymbolicLink()when scanning~/.claude/projects/(or follow symlinks withstat())/resume, and permissionsConfirming this also affects plain git worktrees on Linux (no devcontainer involved).
My setup: a simple git worktree add on a native Linux host. Memory files were saved to the CWD-derived path (-home-user-src-myproject-worktrees-feature-branch/memory/), but in a later session the system prompt pointed the LLM to the git-root-derived path (-home-user-src-myproject/memory/), which was empty.
Previously saved memories became orphaned and invisible.
This confirms Bug 3 from your analysis is reproducible in the simplest possible worktree scenario — no container path remapping needed.
(Original report: https://github.com/anthropics/claude-code/issues/34975)
Closing for now — inactive for too long. Please open a new issue if this is still relevant.
why would you close a bug just because you haven't gotten around to fixing it?
I’m one of the people who was also interested in this bug. I’d like to restate how I see it.
At first, I thought it was bad that the state for different worktree instances—for different worktree—gets saved in the same directory. That is, if I work in different worktree, then, despite being on different branches, it still gets saved in the same directory.
At first I thought that was bad, but then I got used to it, and it seems like it could actually be reasonable. Maybe the developers intended it to work this way, because worktrees are usually created as a temporary copy to implement some feature and then removed.
From that perspective, having the state saved strictly in a single directory for the entire base Git repository on disk seems more correct to me. That’s just my opinion—I’m simply expressing it here.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.