[BUG] Desktop: integrated terminal never binds in a restored long-lived session; each click leaks a shell
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version
What's Wrong?
In a long-lived Code-tab session that has been restored from disk (survived an app restart), the integrated terminal never appears. Clicking to open it spawns a healthy shell every time — the UI just never binds to it. A session created fresh in the same project, in the same app instance, binds on the first try.
From the user's side the button looks dead. From the system's side everything succeeds, which is what makes this hard to notice: shells accumulate silently, one per click.
Evidence
main.log, filtered to startShellPty, over one afternoon:
16:57:03 local_e2702028-… ← long-lived session, restored across an app restart
18:14:54 local_e2702028-…
18:59:17 local_e2702028-…
19:14:55 local_e2702028-…
19:12:27 local_7270a9d7-… ← session created minutes earlier, same project, same app instance
Four requests for the broken session, never binding. One request for the fresh session, bound immediately and worked.
No error is logged after any of the four. The line that follows is unrelated (fetchGitDiff timing…).
Each request produces a correctly configured shell. Sampled while the pane showed nothing:
pid 1082949 stat=Ss+ tty=/dev/pts/11 alive 717s
TERM=xterm-256color COLORTERM=truecolor
cwd=<the session's project directory>
Ss+ with wchan=do_select — a session-leader foreground shell idling at its prompt. Writing directly to /dev/pts/11 from outside confirms the slave side is live.
Both sessions have identical cwd and permissionMode: auto in ~/.config/Claude/claude-code-sessions/<account>/<workspace>/local_*.json. The only difference is age: the broken one predates the app restart, the working one was created after it.
What Should Happen?
A restored session's terminal pane should bind to the pty it just requested. Failing that, a repeated startShellPty for a session that already has an unbound pty should either reuse it or log an error, rather than silently leaking a shell per click.
Reproduction
- Open a Code-tab session and leave it running long enough to be persisted.
- Quit and relaunch the desktop app so the session is restored from disk.
- Open the integrated terminal in the restored session — nothing renders.
- Create a new session in the same project and open its terminal — works.
- Confirm the leak:
ps -eo pid,stat,args | grep 'bash -l'shows one new shell per click in step 3.
Environment
- Claude Desktop: 1.24012.11 (Linux beta, apt)
- Embedded CLI: 2.1.219
- Linux Mint 22.3 (Ubuntu 24.04 base), x86_64, X11, Cinnamon
- Shell: bash