[BUG] resume enumeration returns zero sessions while `claude --resume <id>` resumes the same transcripts fine

Status Open
Reported on v2.1.203
Maintainer reply None cached
Activity 4 comments · opened Oct 2, 2026

Bug Description

/resume and claude --resume both report no previous sessions in a project that has 43 intact
transcripts on disk, while claude --resume <session-id> resolves and resumes those same
transcripts perfectly. Enumeration returns zero; direct-by-ID lookup works. The result is a
convincing data-loss scare with no actual data loss.

Steps to Reproduce

I do not have a minimal repro — that's largely why I'm filing, since I was able to eliminate the
obvious causes but not reach the mechanism. In the affected project:

  1. cd /path/to/project (43 *.jsonl transcripts in ~/.claude/projects/<escaped-cwd>/, newest 14 days old)
  2. claude --resume → picker is empty. /resume from inside a session → same.
  3. claude --resume 12666795-f1bf-4ddc-b146-3db79bfad7f5 --fork-session --print "Reply with exactly: DISCOVERY_OK"

→ resumes correctly, prints DISCOVERY_OK, exit 0.

Expected Behavior

Any session claude --resume <id> can resolve should be listed by claude --resume.

Actual Behavior

Zero sessions listed. 43 transcripts (222 B – 6.6 MB, 37 MB total, Aug 16 – Sep 18), of which 38
have real user messages.

What I ruled out

  • Symlinked project directory. ~/.claude/projects/<escaped-cwd> is a symlink (the transcript dir is

kept in a git repo so it syncs across machines). fs.readdirSync(projects, {withFileTypes: true})
reports it as isDirectory: false, isSymbolicLink: true, which looked like the cause, but it is not:
I built two scratch projects, made one a real directory and the other a symlink to an external
directory, and claude --continue resumed correctly in both.

  • Age / staleness. Untested — see below. This is my leading remaining hypothesis: every session

that does enumerate in my tests was minutes old, and every session that fails to enumerate is
≥14 days old.

  • #98384 (sessions started in a subdirectory). Not this. All 39 sessions record the repo root

as their first cwd, confirmed by parsing every transcript.

  • Transcript size (floated as a possible factor in #98384). Not this either — 40 of 43 are under

2.8 MB, and the smallest is 222 B.

  • #95482 (first user message is a slash command). Not this. 29 of 38 open with ordinary prose.
  • Git branch scoping. 7,655 records say main, which is the current branch.
  • Account/org scoping. Only 12 files carry ownerAccountUuid/ownerOrganizationUuid (claude.ai

bridge sessions), and all share one account and org.

  • Retention/pruning and config. cleanupPeriodDays is 180; ~/.claude.json still has the project

entry; files back to Aug 16 are present and readable.

Additional Context

  • Permissions are 0600, owner matches, and the running session writes into the same directory fine.
  • Transcripts carry version values 2.1.203, 2.1.233 and 2.1.234; the installed build has been

2.1.234 since Aug 17, so this is not an upgrade-boundary artifact.

  • I could not test the age hypothesis: the auto-mode classifier blocked backdating a scratch

transcript's mtime, planting a decoy, and copying a transcript into a test project — reasonably, since
transcript and timestamp manipulation is exactly what it should be suspicious of. If someone on the
team wants that datapoint, backdating a single scratch transcript's mtime and running
claude --continue should settle it in about a minute.

  • Possibly related in spirit, though a different surface: #96559 (session intact on disk but absent

from the picker).

  • Suggestion regardless of cause: when enumeration finds zero sessions in a project directory that

contains *.jsonl files, say so — "43 transcripts found, 0 listed" would have turned an hour of
black-box bisection into a one-line diagnosis.

Environment Info

  • Platform: Linux 7.2.8-2-cachyos (CachyOS)
  • Terminal: xterm-256color
  • Version: 2.1.234
  • Install: ~/.local/share/claude/versions/2.1.234 (single binary), launched via ~/.local/bin/claude

View original on GitHub ↗

3 Comments

dudehook · 13 days ago

Correction to my own report — please disregard the "symlinked project directory" exclusion above.

The two tests I used to rule out the symlink were both invalid, in the same way. I built the scratch
projects with claude --print, which produces sessions marked promptSource: "sdk" /
entrypoint: "sdk-cli", and I probed them with claude --continue. So:

  1. --continue resolves the project path from the cwd and never enumerates ~/.claude/projects/, so it

cannot distinguish a symlinked project dir from a real one. It was the wrong probe for an
enumeration bug.

  1. The picker appears to exclude non-interactive sessions, so my fresh "real directory" control was

invisible to the picker for a reason unrelated to what I was testing. The real sessions are all
promptSource: "typed" / entrypoint: "cli".

So the symlink is not ruled out, and neither is the age hypothesis. What remains solid is the core
report: enumeration returns zero while claude --resume <id> resumes the same transcripts, all 43
transcripts parse as well-formed JSON, every session records the repo root as its first cwd, and the
exclusions based purely on reading the transcripts (branch, account/org, size, slash-command-first,
retention) still hold.

One new datapoint: Ctrl+A ("show all projects") also lists nothing. In this setup that is consistent
with a symlink-skipping readdir, because the only project directory containing transcripts is the
symlinked one and the only real project directory has zero transcripts — so there is nothing left for
an all-projects view to show. fs.readdirSync(projectsDir, {withFileTypes: true}) reports that entry as
isDirectory: false, isSymbolicLink: true; if the enumerator filters on isDirectory(), the project is
skipped wholesale while direct-by-ID lookup still succeeds. That is a hypothesis, not a finding — it
needs an interactive-session control, which I'm running next and will report either way.

Apologies for the noise in the original exclusion list.

dudehook · 13 days ago

Confirmed, with a minimal repro: a symlinked project directory is hidden from the all-projects
(Ctrl+A) view, while the project-scoped view still lists it.

This is narrower than what I originally filed, and it is a different bug from the one in the title.
Posting it separately because it reproduces cleanly in isolation.

Repro, one session, one variable:

  1. mkdir -p ~/temp/resume-probe && cd ~/temp/resume-probe && claude — interactive, send one prompt, exit.

(It must be interactive. A --print session is not listed by the picker at all, which is what
invalidated my first attempt at this test.)

  1. cd ~/temp/resume-probe && claude --resume → the session is listed. Ctrl+A → also listed.
  2. Convert the project directory to a symlink, changing nothing else:

``
mv ~/.claude/projects/-home-dhooker-temp-resume-probe ~/temp/resume-probe-store
ln -s ~/temp/resume-probe-store ~/.claude/projects/-home-dhooker-temp-resume-probe
``

  1. cd ~/temp/resume-probe && claude --resume → still listed (project-scoped lookup follows the symlink).

Ctrl+A → not listed (the all-projects walk skips it).

Same file, same cwd, same session, same mtime. Only the directory entry type changed.

Mechanism, consistent with the asymmetry: the project-scoped path builds the directory from the cwd and
opens through the symlink transparently, whereas the all-projects walk enumerates ~/.claude/projects/
and filters entries by type. fs.readdirSync(dir, {withFileTypes: true}) reports a symlinked
subdirectory as isDirectory: false, isSymbolicLink: true, so an isDirectory() filter drops the whole
project. Either stat the entry (following links) or accept isSymbolicLink() entries that resolve to
directories.

This matters for a real workflow: keeping ~/.claude/projects/<escaped-cwd> as a symlink into a git repo
is how you sync transcripts, memory and settings across machines, since Claude keys them by absolute
path. That setup silently loses the all-projects view.

Separately, the originally-filed bug is still unexplained and is not this. In the affected project the
project-scoped view is empty, and the symlinked probe above proves a symlink alone does not cause that.
Also now ruled out by direct test: mtime staleness — backdating the probe transcript's mtime to match
the age of the affected project's newest session (14 days) left it listed. Remaining untested differences
between the probe and the affected project: 43 files / 37 MB total / 6.6 MB largest versus one 25 KB file,
16 session subdirectories, internal timestamps ending 14 days ago, and transcripts written by three
different versions. I'm bisecting those with synthetic files and will report.

dudehook · 13 days ago

Closing out what I can test from outside the source, so the eliminations aren't repeated.

Also ruled out by direct test, using a synthetic store built to match the affected project:

  • File count. 43 transcripts in the probe project → all 43 listed in the project-scoped picker.
  • mtime staleness. Backdating a transcript to 14 days old (the age of the affected project's newest

session) left it listed.

Established:

  • Symlinked project directory → hidden from the all-projects (Ctrl+A) view, listed normally in the

project-scoped view. Minimal repro in the comment above. This is the one solid, actionable bug here.

  • claude --resume <id> resolves and resumes every affected transcript correctly, so the data is intact

and reachable; only enumeration is affected.

  • --print / SDK sessions (promptSource: "sdk", entrypoint: "sdk-cli") are not listed by the picker

at all. Expected behavior, probably, but worth knowing — it silently invalidates any repro attempt that
builds its fixture with claude --print, which cost me two wrong conclusions here.

Still unexplained: the project-scoped picker is empty in the affected project (43 transcripts,
37 MB, newest 14 days old, all well-formed JSON, all recording the repo root as their first cwd) while
a symlinked probe project in the same config directory lists fine. Untested remainders are total volume,
per-file size, and transcripts written by older versions (2.1.203 / 2.1.233 alongside 2.1.234). I've
stopped bisecting rather than keep running interactive rounds against a black box — whoever has the
enumeration code should be able to see it immediately given the eliminations above.

Happy to run a specific experiment or hand over a transcript sample if that helps. The one change I'd
still ask for regardless of root cause: when enumeration yields zero sessions in a project directory that
contains *.jsonl files, say so. "43 transcripts found, 0 listed" would have replaced this entire
investigation with a one-line diagnosis, and the silent empty list reads to a user as total history loss.

Showing cached comments. Read the full discussion on GitHub ↗