google-gemini / google-gemini/gemini-cli
bug(cli): non-atomic state.json write can corrupt and silently wipe persistent state
- 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
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