anomalyco / anomalyco/opencode
cli: concurrent session.json writes race on Windows → EPERM, TUI loses current session
@Hona is already working on this.
Since Sep 5, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Summary
Concurrent opencode2 CLI processes on Windows race on the TUI state file: the atomic rename of session.json.<pid>.<uuid>.tmp → session.json fails with EPERM, surfacing as an unhandled rejection (role=cli). The write is lost and the terminal no longer shows the current session.
Environment
- opencode version: 0.0.0-beta-19151
- OS: Microsoft Windows [Version 10.0.26200.9168]
- Terminal: Windows Terminal (
TERM=xterm-256color,WT_SESSIONset) - Shell:
C:\Windows\system32\cmd.exe(%ComSpec%); commands run via PowerShell 5.1 - Install/channel: beta
- Active plugins:
@prevalentware/opencode-goal-plugin@0.1.45(viacli.jsonplugins); local file pluginopenrouter-retry-guard(~/.config/opencode/plugin/openrouter-retry-guard.ts);~/.config/opencode/plugins/empty;opencode.jsonlists no plugins
Reproduction
- On Windows, open an interactive
opencode2TUI (shared background service). - Concurrently run repeated
opencode2 run ...invocations against the shared service (e.g., an automation loop every ~25s; with ~8 concurrent clients it reproduces fast — 9 collisions observed within a single second). - Watch
~/.local/share/opencode/log/opencode.logforunhandled rejection ... EPERM ... session.json. - The TUI no longer shows the current session.
Expected Behavior
State writes from concurrent CLI processes should be serialized (lock file / retry with backoff on EPERM), so no write is lost, no unhandled rejection is logged, and the current session stays visible in the terminal.
Actual Behavior
The rename fails and the error escapes as an unhandled rejection; the session.json update is dropped and the current session disappears from the terminal. Exact error (paths normalized):
timestamp=2026-09-05T14:11:42.677Z level=ERROR run=9407f77e message="unhandled rejection" cause="Error: EPERM: operation not permitted, rename '~/.local/state/opencode/session.json.17072.9354ca59-7ab6-4dd3-9014-eb81bfe24c84.tmp' -> '~/.local/state/opencode/session.json'" role=cli
Same signature in bursts on 2026-08-19 (9 distinct PIDs, same second), 2026-08-20, and 2026-09-05. After the failure session.json still held the stale 13-byte content ({"pinned":[]}).
Additional Context
- Intermittent, load-dependent: single interactive TUI alone rarely triggers it; concurrent
runclients make it near-deterministic. - The shared background service stayed healthy throughout (same PID,
/api/health200, model catalog 200), isolating this to the CLI state-write path rather than the service. - Local workarounds that helped: routing single-shot automation through
opencode2 run --standalone(no shared-state contact) and a pending Windows Defender exclusion for the state dir (needs admin;Add-MpPreferencefails without elevation). - Happy to provide fuller redacted log excerpts on request.
Suggested Resolution
- Serialize CLI state writes across processes: guard the read-modify-write of
session.jsonwith an interprocess lock (lockfile with PID + stale-lock expiry for crash safety). - Retry the tmp→final rename with backoff on
EPERM/EACCES(Windows AV/indexer locks are usually transient): e.g. 3–5 attempts, ~50–200 ms jittered delay, then a singleWARN— never anunhandled rejection. - A TUI-state persist failure must degrade to in-memory state, never escape as an unhandled rejection or drop the visible current session.
- Longer term: single-writer design where the background service owns
session.jsonand CLI processes send update RPCs instead of writing the file directly. - Regression test: spawn N concurrent writers against one state dir on Windows CI and assert no
EPERMescapes and the final file parses.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.