google-gemini / google-gemini/gemini-cli

bug(cli): non-atomic state.json write can corrupt and silently wipe persistent state

Open
#29,307 1 comment 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

## What happened?

`packages/cli/src/utils/persistentState.ts:55-63` writes `state.json` in place:

```ts
fs.writeFileSync(filePath, JSON.stringify(this.cache, null, 2));
```

No tmp+rename, no fsync, no lock. Kill / power-loss / disk-full mid-write leaves truncated JSON; next `load()` (`persistentState.ts:47-50`) catches and resets to `{}` — silently wiping banner/tips/warning counts. Same in-place pattern in `packages/cli/src/utils/commentJson.ts:43-46` for `settings.json` (user data loss, worse).

## What did you expect to happen?

Atomic persist: write `state.json.tmp.` + `fsync` + atomic `renameSync`, keep `state.json.bak`; concurrent CLI instances need lockfile or last-writer-wins handling instead of silent reset.

## Client information

- Repo: `google-gemini/gemini-cli` @ `main` `9c1b0a610`
- Area: `packages/cli/src/utils/persistentState.ts:55-63` (also `packages/cli/src/utils/commentJson.ts:43-46`)
- Platform: all (crash-window bug)

## Login information

N/A — local state file path via `getPath()`.

## Anything else we need to know?

**Fix direction:** tmp+fsync+rename + `.bak`; on `JSON.parse` failure preserve corrupt copy as `.corrupt` and surface instead of silent `{}` reset. Regression test: truncated `state.json` fixture → backup preserved + state recovered, not wiped. `commentJson.ts` should get the same treatment as a follow-up (user config is higher stakes).

Contributor guide

Open the contributing guide

Research direction

Start with packages/cli/src/utils/persistentState.ts, especially load() at lines 47-50 and the write at lines 55-63, then compare the in-place write in packages/cli/src/utils/commentJson.ts:43-46. Add a regression test using a truncated state.json fixture; done means the corrupt copy is preserved, state is recovered rather than silently reset, and persistence is atomic with a backup.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.