What happened?
When a session is resumed, the conversation itself continues on the resumed session, but some session-scoped storage paths appear to stay bound to the fresh startup session ID that was created before resume was processed.
That means the resumed chat can end up with split state:
- the conversation/history continues under the resumed session
- but plan files, tracker state, and task artifacts can still be written under the startup-only session namespace
From a user perspective, this can make resumed work look inconsistent or partially "lost":
- plan mode files may not be where the resumed session logically expects them
- task tracker continuity can break across resume
- session-scoped artifacts can be orphaned under a different session directory
This appears to affect current upstream main as of commit a93a1ebd on April 3, 2026.
What did you expect to happen?
After resuming a session, all session-scoped state should move with that resumed session consistently.
In other words, once Gemini CLI resumes session X, the CLI should use session X for:
- chat recording
- plan mode storage
- tracker storage
- task storage
- any cleanup logic for session-scoped artifacts
There should not be a split where the resumed conversation uses one session ID while storage-backed workflow state continues using another.
Client information
Client Information
> /about
About Gemini CLI
CLI Version 0.36.0-nightly.20260317.2f90b4653
Git Commit a93a1ebd6
Model Auto (Gemini 3)
Sandbox no sandbox
OS win32
Auth Method Signed in with Google
Tier Gemini Code Assist in Google One AI Pro
Login information
- Auth Method:
Signed in with Google
- Tier:
Gemini Code Assist in Google One AI Pro
Anything else we need to know?
I searched for existing issues first and found related-but-different reports, but not a direct duplicate of this specific storage/session split:
#21311 covers resumed sessions becoming invalid / disappearing after auth failure
#21359 covers plan mode writes failing when sessionId is missing
#24532 covers sessions that appear to have never been recorded / cannot be resumed
This issue is different: resume itself can succeed, but resumed workflow state appears to stay partially bound to the startup session ID.
Technical analysis
The current implementation appears to construct Storage once using the startup session ID, then later mutate only the config session ID during resume:
packages/core/src/config/config.ts:1257
this.storage = new Storage(this.targetDir, this._sessionId);
packages/cli/src/gemini.tsx:592
config.setSessionId(resumedSessionData.conversation.sessionId);
packages/cli/src/ui/hooks/useSessionBrowser.ts:71
config.setSessionId(existingSessionId);
packages/core/src/config/config.ts:1733
setSessionId(sessionId: string): void { this._sessionId = sessionId; }
But Storage keeps its own captured readonly session ID:
packages/core/src/config/storage.ts:31
private readonly sessionId: string | undefined;
packages/core/src/config/storage.ts:36
constructor(targetDir: string, sessionId?: string) { ... this.sessionId = sessionId; }
And the session-scoped storage paths continue to derive from that captured Storage.sessionId:
packages/core/src/config/storage.ts:309
packages/core/src/config/storage.ts:316
getProjectTempTrackerDir()
packages/core/src/config/storage.ts:343
The tracker service is also cached off the storage-derived tracker path:
packages/core/src/config/config.ts:2753
packages/core/src/config/config.ts:2756
this.storage.getProjectTempTrackerDir()
And the prompt wiring exposes that tracker directory to the model:
packages/core/src/prompts/promptProvider.ts:76
context.config.storage.getProjectTempTrackerDir()
So the current behavior appears to be:
- CLI starts with fresh startup session ID
A
Storage is constructed using A
- user resumes existing session
B
- config session ID changes to
B
- storage-backed session paths can still resolve using
A
This creates a split between the resumed conversation session and the storage-backed workflow session.
Why this matters
This is especially noticeable in workflows that rely on resume plus persistent state, for example:
- resuming a session and continuing in plan mode
- resuming a session and using the task tracker
- resuming a session and expecting later cleanup to match the resumed session's artifacts
The bug is subtle because the resumed chat itself may look correct while plan/tracker/task state drifts into a different directory tree.
Suggested reproduction
I have not included a full end-to-end runtime repro here, but the logic looks straightforward to exercise:
- start a session and create plan/tracker/task state
- quit the CLI
- start Gemini CLI again so it gets a fresh startup session ID
- resume the previous session via
--resume or the session browser
- continue using plan mode or tracker-backed workflow features
- inspect
~/.gemini/tmp/<project>/... and compare the resumed session ID versus the directories being used for plans/tracker/tasks
Expected fix direction
One of these should happen:
- recreate or rebind
Storage when setSessionId() is called during resume
- make
Storage read the current config session ID dynamically rather than capturing it only once
- otherwise ensure all session-scoped services are refreshed after resume so they point at the resumed session namespace
Right now, Config.setSessionId() appears to update only config state, not the already-constructed storage/session-scoped services.
What happened?
When a session is resumed, the conversation itself continues on the resumed session, but some session-scoped storage paths appear to stay bound to the fresh startup session ID that was created before resume was processed.
That means the resumed chat can end up with split state:
From a user perspective, this can make resumed work look inconsistent or partially "lost":
This appears to affect current upstream
mainas of commita93a1ebdon April 3, 2026.What did you expect to happen?
After resuming a session, all session-scoped state should move with that resumed session consistently.
In other words, once Gemini CLI resumes session
X, the CLI should use sessionXfor:There should not be a split where the resumed conversation uses one session ID while storage-backed workflow state continues using another.
Client information
Client Information
Login information
Signed in with GoogleGemini Code Assist in Google One AI ProAnything else we need to know?
I searched for existing issues first and found related-but-different reports, but not a direct duplicate of this specific storage/session split:
#21311covers resumed sessions becoming invalid / disappearing after auth failure#21359covers plan mode writes failing whensessionIdis missing#24532covers sessions that appear to have never been recorded / cannot be resumedThis issue is different: resume itself can succeed, but resumed workflow state appears to stay partially bound to the startup session ID.
Technical analysis
The current implementation appears to construct
Storageonce using the startup session ID, then later mutate only the config session ID during resume:packages/core/src/config/config.ts:1257this.storage = new Storage(this.targetDir, this._sessionId);packages/cli/src/gemini.tsx:592config.setSessionId(resumedSessionData.conversation.sessionId);packages/cli/src/ui/hooks/useSessionBrowser.ts:71config.setSessionId(existingSessionId);packages/core/src/config/config.ts:1733setSessionId(sessionId: string): void { this._sessionId = sessionId; }But
Storagekeeps its own captured readonly session ID:packages/core/src/config/storage.ts:31private readonly sessionId: string | undefined;packages/core/src/config/storage.ts:36constructor(targetDir: string, sessionId?: string) { ... this.sessionId = sessionId; }And the session-scoped storage paths continue to derive from that captured
Storage.sessionId:packages/core/src/config/storage.ts:309getProjectTempPlansDir()packages/core/src/config/storage.ts:316getProjectTempTrackerDir()packages/core/src/config/storage.ts:343getProjectTempTasksDir()The tracker service is also cached off the storage-derived tracker path:
packages/core/src/config/config.ts:2753getTrackerService()packages/core/src/config/config.ts:2756this.storage.getProjectTempTrackerDir()And the prompt wiring exposes that tracker directory to the model:
packages/core/src/prompts/promptProvider.ts:76context.config.storage.getProjectTempTrackerDir()So the current behavior appears to be:
AStorageis constructed usingABBAThis creates a split between the resumed conversation session and the storage-backed workflow session.
Why this matters
This is especially noticeable in workflows that rely on resume plus persistent state, for example:
The bug is subtle because the resumed chat itself may look correct while plan/tracker/task state drifts into a different directory tree.
Suggested reproduction
I have not included a full end-to-end runtime repro here, but the logic looks straightforward to exercise:
--resumeor the session browser~/.gemini/tmp/<project>/...and compare the resumed session ID versus the directories being used for plans/tracker/tasksExpected fix direction
One of these should happen:
StoragewhensetSessionId()is called during resumeStorageread the current config session ID dynamically rather than capturing it only onceRight now,
Config.setSessionId()appears to update only config state, not the already-constructed storage/session-scoped services.