Description
Description
When a second OpenCode server shares the data directory, the managed service's startup recovery resumes sessions that the other server is still running. It marks their in-flight tool calls as aborted, adds "The server restarted while you were working", and starts its own step, so two servers drive one session.
Execution claims only store a timestamp, so the sweep can't tell a dead owner from a live one. restart.ts assumes "unregistered servers sharing the database never sweep", but their live claims still get swept by the service.
What I saw: a desktop client runs its own bundled opencode serve --port <n>, and the background service started while that server was mid-turn. When the original server's subagents finished, its next request failed with:
Anthropic Messages system updates cannot split a local tool call from its tool result
because the history now had the service's unresolved tool calls in it. The session stayed stuck until I sent another message. I didn't hit this on V1.
Plugins
None (isolated repro)
OpenCode version
2.0.16 (also seen with a 2.0.15 client-bundled server next to a 2.0.16 service)
Steps to reproduce
- Use fresh
XDG_CONFIG_HOME/XDG_DATA_HOME/XDG_STATE_HOME/XDG_CACHE_HOMEdirs and an Anthropic model. - Start server A:
opencode serve --port 4801. - On A, prompt: "Call the subagent tool twice in parallel. Each subagent runs
sleep 150 && echo done." - While the subagents are running, start server B on the same dirs:
opencode serve --service --port 4799. - B marks A's subagent calls as errors, adds the restart notice, and starts new subagents.
- When A's subagents finish, A's next step fails with the error above.
Screenshot and/or share link
No response
Operating System
macOS 26 (Darwin 25.3.0, arm64)
Terminal
N/A (API-driven repro)
Activity
gszep commentedon Sep 28, 2026
Thanks for the detailed report and #51215. I confirmed overlapping executions on a local 2.0.15-based build sharing its DB between an unregistered server and the managed service; each failed request preceded the other server’s tool completion. I am carrying both commits from #51215 (46 execution tests pass). There is also a separate deterministic lowering problem: an update followed by an available or repaired error result still throws. Repro in #51764; fixing that does not replace the live-owner recovery guard.
nkoynov commentedon Oct 7, 2026
Hit this from T3 Code (2.0.22; restart.ts is unchanged in 2.0.24). The second server was started by an agent: in a thread served by T3's own
opencode serve --hostname 127.0.0.1 --port N, the model ranopencode mcp listto debug an MCP login, and the CLI auto-startedopencode serve --service. Its boot sweep resumed both sessions T3's server was running ("The server restarted while you were working" 0.15 s after start), and for 3h22m the two servers alternated steps. Each had a different MCP set, so one session got 437 full Code Mode catalog notices (~4 MB stored) and was auto-compacted.Repro on 2.0.24 (VM): T3 thread runs
sleep 120; date >> runs.txt→opencode mcp listin a shell → the service marks the running shell call "Tool execution interrupted" whilesleepkeeps running, the resumed model starts the same command again (twosleepprocesses, two lines in runs.txt, twoidlerows for one turn).Workaround until #51215 lands:
{"disabled": true}in~/.config/opencode/service.json. CLI commands then use a standaloneserve --stdio, which never sweeps; same repro: onesleep, one line, no notice. Onlyopencode service start/restart,pairandserve --servicestill start the managed service.Starefossen commentedon Oct 8, 2026
Also on 2.0.24 (macOS arm64): two managed services with separate XDG state/config but a shared
opencode.db. Service B's boot log showsFailed to drain Sessionfor two sessions still running in A.Another effect: the resumed turn runs with B's tools, cwd and sandbox. A turn started in a sandboxed server can continue in an unsandboxed one.
Source:
restart.ts:192-231only checks the localexecution.active. Fix: record the claim owner (server id + heartbeat) and skip claims whose owner is alive.jorgeferreira1 commentedon Oct 25, 2026
Still happening on 2.1.4 (Linux). This time the trigger was an auto-update, and the session that got taken over belonged to a live
opencode run --standalone.opencode run --standalone -m <model> "<task>", with its ownserve --stdio. It was 7 minutes into a long shell call.updated OpenCode from=2.1.3 to=2.1.4 method=curl). The oldserve --serviceshut down, and a 2.1.4serve --servicestarted 5 s later.aborted: Tool execution interrupted: shelland started its own step.So a standalone run can't be bounded by its caller while any
serve --serviceshares the DB, and an auto-update is enough to set that off without anyone restarting anything. #51215 (skip claims whose owner is alive) would have covered this case.opencodejr commentedon Oct 28, 2026
Thanks for the detailed report. I reproduced this on 2.0.16, and it still happens on the latest
dev(9adc42b).I used a mock OpenAI-compatible model instead of Anthropic and drove both servers through the HTTP API:
opencode serve --port 4801on fresh XDG dirs, and prompt a session so that it runs a shell command,sleep 40 && echo done.opencode serve --service --port 4799on the same dirs.sleepprocess keeps running.I couldn't see the Anthropic-specific error without Anthropic credentials, but the two servers driving one session, which is what leads to it, reproduces. This contradicts the assumption in
packages/core/src/session/execution/restart.tsthat every unreleased execution claim belongs to a dead process. The script below reproduces it; it needs the small mock LLM server it starts (mock.ts) in the same directory.Reproduction script (repro.sh)
Run it:
bash repro.sh