[Bug] Inherited stale CLAUDE_CODE_SESSION_ID causes silent transcript loss without warning
Bug Description
Title: Inherited stale/cross-project CLAUDE_CODE_SESSION_ID causes silent transcript loss (no --resume entry, no warning)
What happens:
If a long-lived process is ever started from a shell inside a Claude Code session, it permanently inherits CLAUDE_CODE_SESSION_ID, CLAUDE_CODE_CHILD_SESSION=1, CLAUDE_CODE_EXECPATH, etc. Every claude later launched as a descendant of that process
treats itself as a child of the original session — which by then is dead and belongs to a different project — and writes no transcript to ~/.claude/projects/.... The session runs and responds normally; it just silently never persists and never
appears in claude --resume. No warning is shown, so it's indistinguishable from data loss.
In my case the long-lived process was the Plank dock (started once from inside a Claude session during a reboot recovery). It then poisoned every app launched from the dock — including the IDE → its integrated terminal → claude. All terminal
sessions in that IDE stopped persisting, while plain-terminal sessions were unaffected.
Environment:
- Claude Code 2.1.177, native install, claude doctor clean
- Linux (Ubuntu Budgie), JetBrains/PyCharm integrated terminal
- Inherited vars pointed at a dead session from a different project, created days earlier on v2.1.174
Repro:
- In a Claude session, note $CLAUDE_CODE_SESSION_ID.
- From that shell, launch a long-lived process (e.g. restart your dock/panel/IDE), then exit Claude.
- Launch claude as a descendant of that process (e.g. a terminal it spawns). It inherits the old CLAUDE_CODE_SESSION_ID and CLAUDE_CODE_CHILD_SESSION=1.
- Hold a conversation → no .jsonl appears in the project dir, and the session is absent from claude --resume.
- unset CLAUDE_CODE_SESSION_ID CLAUDE_CODE_CHILD_SESSION CLAUDE_CODE_EXECPATH CLAUDE_CODE_SSE_PORT before claude → persistence returns.
Expected:
When claude starts with an inherited CLAUDE_CODE_SESSION_ID that is stale (its session is gone) or belongs to a different project/cwd, it should start a fresh persisted session — or at minimum print a visible warning that the conversation will not
be saved. Today it silently produces no transcript and no --resume entry, which looks identical to data loss.
Workaround (environment-side, for reference):
The stale vars lived in a long-running desktop process (the Plank dock) that had been started from inside a Claude session, so it poisoned every app it launched. Cleared without logging out by relaunching the dock from the systemd user manager's
clean environment:
```
pkill -x plank; sleep 1
systemd-run --user --unit=plank-dock1 --collect plank
systemd-run --user --unit=plank-dock2 --collect plank -n dock2
then fully quit and relaunch the IDE from the now-clean dock
After this, the IDE terminal showed only a fresh CLAUDE_CODE_SSE_PORT (integration still active) and claude persisted normally. This is a user-side mitigation; it does not address the silent-failure behavior in the CLI.
**Environment Info**
- Platform: linux
- Terminal: gnome-terminal
- Version: 2.1.177
- Feedback ID: 32085ba1-6bd1-4518-8378-3d8772b2c12f
**Errors**
```json
[]Showing cached comments. Read the full discussion on GitHub ↗
5 Comments
This is a sharp diagnosis, and I can confirm the poisoning vector from my own machine. In a shell running inside Claude Code, exactly these are exported:
Any process spawned from that shell inherits all of them by ordinary POSIX inheritance — so your chain (Claude session → dock → IDE → integrated terminal →
claude) carrying a dead, cross-projectCLAUDE_CODE_SESSION_IDis fully consistent with this. The descendant treats itself as a child of a session that no longer exists, and silently writes no transcript.User-side detection (works today, before the upstream fix): in any terminal where persistence matters, check whether it's poisoned before trusting it —
If you see
CLAUDE_CODE_SESSION_ID/CLAUDE_CODE_CHILD_SESSIONin a terminal you did not just launch from insideclaude, that terminal'sclaudesessions won't persist.User-side fix / workaround:
The durable fix is to start long-lived processes (dock/panel/IDE) from a shell that was not spawned inside a Claude session, so they never capture these vars in the first place.
Confirm it actually persisted (the same silent-non-persistence failure mode as #68435): after a session, check that a real transcript is growing, not a stub —
A healthy session's
.jsonlis KB–MB. If nothing new appears (or it's frozen at ~110 bytes), the session never persisted — which is the data loss, made visible.What's verified vs. upstream: the env inheritance and the
unsetworkaround I confirmed directly. What only Anthropic can fix is the silent part — a descendant inheriting a stale/cross-projectCLAUDE_CODE_SESSION_IDshould detect that the parent session is dead (or belongs to another project) and either start a fresh persisted session or warn, instead of running without ever writing a transcript. Until then, theenv | grepcheck is the cheapest guard against mistaking this for permanent data loss.Thank you, but I can not take the credit. Claude did most of the heavy lifting here. I just asked (hopefully) insightful questions and added context and guidance.
Indeed, yes, but seems to have the side effect of breaking the IDE integration, if, for example, running inside a PyCharm terminal.
Startup should validate inherited Claude session env before suppressing transcript creation. If
CLAUDE_CODE_CHILD_SESSIONorCLAUDE_CODE_SESSION_IDis set, check that the parent session exists, is active or recently active, and matches cwd/project lineage.On mismatch, warn and start a new top-level session while recording
stale_env_session_id. That preserves child-session behavior for real descendants but prevents a dock/IDE process from turning future sessions into silent non-persisted children.---
_Generated with ax._
We hit what looks like the same root cause with a different symptom: two concurrently live sessions sharing one
CLAUDE_CODE_SESSION_ID.Setup: several parallel
claudesessions in separate terminal tabs (macOS/iTerm, native install; some tabs launched viacaffeinate claude). One tab was spawned from a non-clean environment (a shell descended from another live session, or a restored pane), so its environment already carried a live sibling session'sCLAUDE_CODE_SESSION_ID. The freshclaude(a brand-new conversation, no--resumeand no--session-id) adopted the inherited id instead of minting a new one.What we observed:
.session_idin PreToolUse stdin JSON) carried the correct, distinct id for each session — only the env var was wrong. That divergence (env id ≠ hook-visible id) turned out to be a useful detection signal, and might be one for you too.CLAUDE_CODE_SESSION_IDunset), consistent with your finding that the leak rides long-lived intermediary processes.+1 to the expected behavior as stated, with one addition: on startup with an inherited
CLAUDE_CODE_SESSION_IDthat is (a) stale, (b) from a different project/cwd, or (c) currently in use by another live session,claudeshould mint a fresh id absent an explicit--resume/--session-id— or at minimum print a visible warning. Case (c) is the one that bit us: the inherited id wasn't stale, it was live, so the two sessions collided while both were running.Thanks for the detailed report and repro — I retested this on the released 2.1.233 on Linux.
What I found:
CLAUDE_CODE_SESSION_ID=<stale> CLAUDE_CODE_CHILD_SESSION=1 claude -p "say hi"now generates a fresh session id and does write a transcript under~/.claude/projects/….--resumelist) — but it's no longer silent: the footer shows "Transcript saving is off — inherited CLAUDE_CODE_CHILD_SESSION marker · restart with CLAUDE_CODE_FORCE_SESSION_PERSISTENCE=1 to keep future transcripts".CLAUDE_CODE_SESSION_ID(no child marker) has no effect — a fresh id is used and the transcript is saved.The silent-loss behavior you hit was fixed in 2.1.217 ("Added warnings when … session saving is off due to an inherited environment variable, instead of losing transcripts silently" — changelog). For a top-level session that inherited the marker from a long-lived launcher/dock, set
CLAUDE_CODE_FORCE_SESSION_PERSISTENCE=1, or removeCLAUDE_CODE_CHILD_SESSIONfrom that process's environment.Closing as fixed — reply here if you still see silent transcript loss on 2.1.217 or later and we'll reopen.
🤖 Generated with Claude Code