google-gemini / google-gemini/gemini-cli

ACP: session/load in the same UTC minute as session/new fails with "No previous sessions found" — and durably destroys the session's resumability

Open
#28,693 3 comments 0 reactions 0 assignees View on GitHub
area/core effort/medium kind/bug priority/p1 status/bot-triaged
Dominant language
TypeScript
Stars
107k
Forks
14.6k
Avg merge
2d 3h
Merged PRs (30d)
45

Description

> [!NOTE]
> **Updated 2026-08-09 — root cause identified; original framing corrected.** The original report claimed a crashed session is "never persisted". That's wrong: persistence is fine (every record is written synchronously, and the conversation file survives any kill). The real defect is in the **load path**: `session/load` eagerly re-initializes chat recording for the requested session id *before* resolving it, appending a fresh metadata header + `$set {messages: []}` checkpoint to `session--.jsonl`. When the load runs in the **same UTC minute** the session was created, that append lands in the session's own conversation file; the checkpoint buries the conversation in the fold, `hasResumableContent` flips false, the session is filtered from the listing, and the load fails with `No previous sessions found for this project` — **after durably destroying the session's resumability**. No crash is required (it reproduces after a clean exit). Full mechanism with source refs at `cf22ac7`, a three-variant repro on 0.54.0, and the likely unification with #27913: [see this comment](https://github.com/google-gemini/gemini-cli/issues/28693#issuecomment-5233382273).

### What happened

When `gemini` runs as an ACP agent, `initialize` advertises `agentCapabilities.loadSession: true` — but a `session/load` issued in the same UTC minute as the session's `session/new` fails with:

```
No previous sessions found for this project
```

and durably poisons the session file, so every later `session/load` for that id fails too. Automated ACP clients (an editor reconnecting right after a crash, scripted harnesses) land in this window routinely; a human retrying minutes later lands in a different minute and sees loads succeed, which makes the failure look intermittent or environment-dependent.

### Version

- Originally observed on `gemini` 0.53.0; root-caused and reproduced on 0.54.0, macOS/arm64, driven over stdio by a minimal ACP client. Mechanism present in source at `cf22ac7`.

### Steps to reproduce (no crash or kill required)

1. Start gemini in ACP mode; `initialize` → `loadSession: true`.
2. `session/new` ({ cwd, mcpServers: [] }) → `sessionId`.
3. Run one or more `session/prompt` turns to completion.
4. End the process — cleanly (close stdin, exit 0) or by SIGKILL of the process tree; both reproduce.
5. Spawn a fresh gemini ACP process; `initialize`; `session/load` ({ sessionId, cwd, mcpServers: [] }) — **within the same UTC minute as step 2**.

### Expected

`session/load` finds the session (its file is on disk with all turns) and replays the conversation per the ACP spec — and, being a read operation, does not modify the session it is resolving.

### Actual

Step 5 fails with `No previous sessions found for this project`, and the session's `session--.jsonl` gains a trailing metadata header + `$set {messages: []}` checkpoint written by the load call itself, permanently burying the conversation in the fold (the turn records remain physically present in the file, before the poison pair). Later `session/load` calls — in any minute — keep failing, each leaving an additional context-only junk file.

### Historical note on the original report

The original report reproduced this via SIGKILL of the whole process tree mid-turn and framed it as missing crash persistence. The tree-kill observation itself stands — each launch self-re-execs a sandbox wrapper, so killing only the spawned pid orphans the real agent, and probes must kill the tree — but neither the kill nor its timing is load-bearing for this bug: the probe's new → turns → kill → load sequence simply always landed inside one minute.

### Related

#27913 is very likely the same root cause observed one release earlier: it was filed against 0.46.0, before `3a13b8eeb` (#27770, first shipped in v0.47.0) added the `hasResumableContent` filter. Pre-filter, the poisoned fold still lists, so `session/load` "succeeds" — replaying exactly one `user_message_chunk` (the ``, which `streamHistory` doesn't filter) while the model's restored history is empty (`convertSessionToClientHistory` filters it out). Post-filter, the identical on-disk state reports `No previous sessions found` instead.

Contributor guide

Open the contributing guide

Research direction

Start at the ACP session/load entry point and inspect how it invokes chat recording before resolving the requested session. Trace hasResumableContent, streamHistory, and convertSessionToClientHistory against the same-minute JSONL reproduction. Done means session/load resolves and replays the existing conversation without appending a metadata or context-only checkpoint or damaging resumability.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
backend-api-design, cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.