[BUG] In-session /resume picker collapses to ~0 rows inside a git worktree and never recovers (Ctrl+W restores; `claude -r` from the shell is unaffected)

Status Fixed / completed
Reported on v2.1.232
Maintainer reply None cached
Activity 1 comment · opened Aug 14, 2026 · closed Aug 17, 2026

Summary

In a repo with multiple git worktrees, the /resume picker invoked from inside a running session paints the full session list, then ~20 ms later collapses to 2–3 rows and stays that way indefinitely (still collapsed at t=30 s). The header loses its (1 of N) counter while collapsed, which is the visible tell.

The same picker launched as claude -r from a plain shell, in the same directory, does not collapse — it briefly passes through the same state and recovers within ~30 ms, showing the full list.

Pressing <kbd>Ctrl</kbd>+<kbd>W</kbd> ("show all worktrees") in the collapsed picker restores the full list instantly, which points at the current-worktree filter rather than at loading or at the transcripts themselves.

The transcripts are all intact on disk. In my case 41 sessions for the current branch, none older than 20 days, were invisible in /resume — easy to misread as the 30-day cleanupPeriodDays cleanup having eaten them.

Environment

  • Claude Code 2.1.232 (native install)
  • Linux 6.8.0-136-generic, zsh, xterm-256color
  • Repo with 13 git worktrees (git worktree list); session started in a linked worktree, not the main one
  • 69 project dirs / 578 .jsonl transcripts under ~/.claude/projects
  • 111 transcripts in the current worktree's project dir, 41 of them on the current branch

Reproduction

Needs a repo with more than one git worktree and a handful of past sessions in the current one.

  1. cd into a linked worktree of that repo
  2. Start Claude normally: claude
  3. Run /resume
  4. Watch the list

Automated repro (drives both paths in a pty and prints header + row count over 30 s; needs pip install pyte):

<details>
<summary><code>resume-worktree-filter.py</code></summary>

#!/usr/bin/env python3
"""
Repro: in-session /resume collapses the session list when the current-worktree
filter is active, and never recovers. `claude -r` from a plain shell does not.

Usage:  python3 resume-worktree-filter.py [/path/to/a/git/worktree]
Requires: pyte  (pip install pyte)
"""
import os, sys, pty, time, select, signal, struct, fcntl, termios

try:
    import pyte
except ImportError:
    sys.exit("pip install pyte")

CWD = sys.argv[1] if len(sys.argv) > 1 else os.getcwd()
COLS, ROWS = 150, 45


def run(argv, feed_resume):
    screen = pyte.Screen(COLS, ROWS)
    stream = pyte.ByteStream(screen)
    pid, fd = pty.fork()
    if pid == 0:
        os.chdir(CWD)
        os.environ["TERM"] = "xterm-256color"
        os.execvp(argv[0], argv)
    fcntl.ioctl(fd, termios.TIOCSWINSZ, struct.pack("HHHH", ROWS, COLS, 0, 0))

    def drain(seconds):
        end = time.time() + seconds
        while time.time() < end:
            r, _, _ = select.select([fd], [], [], 0.1)
            if r:
                try:
                    d = os.read(fd, 65536)
                except OSError:
                    return
                if not d:
                    return
                stream.feed(d)

    def header():
        for line in screen.display:
            if "Resume session" in line:
                return line.strip()
        return "<no picker>"

    def rows():
        return sum(1 for l in screen.display if " ago \xb7 " in l or " ago · " in l)

    if feed_resume:
        drain(14)
        os.write(fd, b"/resume")
        drain(1.5)
        os.write(fd, b"\r")

    for t in (2, 5, 10, 20, 30):
        drain(t - (0 if t == 2 else prev))
        prev = t
        print(f"  t={t:>2}s  header={header()!r}  visible_rows={rows()}")

    os.write(fd, b"\x17")  # Ctrl+W -> show all worktrees
    drain(4)
    print(f"  after Ctrl+W   header={header()!r}  visible_rows={rows()}")

    os.write(fd, b"\x1b")
    drain(1)
    os.write(fd, b"\x1b")
    drain(0.5)
    try:
        os.kill(pid, signal.SIGKILL)
    except Exception:
        pass


print(f"cwd: {CWD}")
print("\nA) interactive session, then /resume  (expected: list collapses and stays collapsed)")
run(["claude"], feed_resume=True)
print("\nB) claude -r from the shell          (expected: list populates normally)")
run(["claude", "-r"], feed_resume=False)

</details>

Observed

cwd: /home/user/projects/myrepo/wt10

A) interactive session, then /resume
  t= 2s  header='Resume session'  visible_rows=3
  t= 5s  header='Resume session'  visible_rows=3
  t=10s  header='Resume session'  visible_rows=3
  t=20s  header='Resume session'  visible_rows=3
  t=30s  header='Resume session'  visible_rows=3
  after Ctrl+W   header='Resume session (1 of 50)'  visible_rows=10

B) claude -r from the shell
  t= 2s  header='Resume session (1 of 51)'  visible_rows=11
  t= 5s  header='Resume session (1 of 51)'  visible_rows=11
  t=10s  header='Resume session (1 of 51)'  visible_rows=11
  t=20s  header='Resume session (1 of 51)'  visible_rows=11
  t=30s  header='Resume session (1 of 51)'  visible_rows=11
  after Ctrl+W   header='Resume session (1 of 116)'  visible_rows=11

Rendered frames from the in-session run, ~20 ms apart. Full list first:

  Resume session (1 of 50)
  ╭──────────────────────────────────────────────────────────────╮
  │ ⌕ Search…                                                    │
  ╰──────────────────────────────────────────────────────────────╯
  ❯ Investigate old session deletion timeline
    10 seconds ago · bg · topic/ABC-1001/security-review · 612.8KB
    Investigate old session deletion timeline
    11 minutes ago · topic/ABC-1001/security-review · 290.8KB
    wt7: implementation
    16 hours ago · topic/ABC-1002/rename-report-columns · 2.4MB
    wt8: plan
    17 hours ago · topic/ABC-1003/enforce-editability-on-create · 1.1MB
    ... 6 more rows ...

then, immediately after, and unchanged for the next 30 s:

  Resume session
  ╭──────────────────────────────────────────────────────────────╮
  │ ⌕ Search…                                                    │
  ╰──────────────────────────────────────────────────────────────╯
    myrepo · wt10
  ❯ Investigate old session deletion timeline
    10 seconds ago · bg · topic/ABC-1001/security-review · 612.8KB
    Investigate old session deletion timeline
    11 minutes ago · topic/ABC-1001/security-review · 290.8KB
    Ctrl+A to show all projects · Ctrl+B to only show current branch · Ctrl+W to show all worktrees · …

What started this investigation was the same collapsed state with <kbd>Ctrl</kbd>+<kbd>B</kbd> (current branch only) also active, where it rendered with zero rows: an entirely empty picker, scope chip myrepo · topic/ABC-1001/security-review · wt10, no sessions at all. For comparison, claude -r in the same directory with the identical scope chip lists 29 sessions.

Expected

The current-worktree filter should not remove sessions that belong to the current worktree, and the picker should not stay in a partially-filtered state. Either the collapsed render should be skipped until the metadata the filter depends on is available, or it should recover when that metadata arrives — as the claude -r path does.

Notes

Two things suggest the filter is applied against session metadata that hasn't been resolved yet, rather than the filter predicate itself being wrong:

  • <kbd>Ctrl</kbd>+<kbd>W</kbd> (bypassing the worktree filter) restores the list instantly and permanently.
  • In the shell (claude -r) path, the identical collapsed frame appears and then recovers ~30 ms later, i.e. the same code path can complete — it just doesn't when the picker is opened from inside a session.

Mechanically similar to #78918 (a picker filter applied to pages before they are fully populated).

The missing (1 of N) counter in the header is a reliable indicator that the visible list is not the real result set — it may be worth not rendering the list at all in that state.

Workarounds

  • <kbd>Ctrl</kbd>+<kbd>W</kbd> in the picker
  • claude -r / claude -r "<search term>" from the shell instead of /resume
  • claude -r <session-id> directly

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗