You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
service: restart leaves stale daemon and duplicated MCP servers running #42270
opencode2 service restart starts a new serve --service daemon without terminating the old one; the stale daemon and its MCP server children keep running, duplicating MCP servers and leaking ~1.5 GB of RAM until manually killed.
Install/channel: local binary in ~/.opencode/bin, channel next
Active plugins: ~/.config/opencode/plugins.v2/memory.ts (opencode-memory)
Reproduction
Configure local MCP servers (e.g. npx -y @playwright/mcp --browser chromium and npx -y @llmindset/hf-mcp-server) in ~/.config/opencode/opencode.jsonc.
Start a session (spawns the serve --service daemon plus the MCP children).
Run opencode2 service restart (or service stop followed by starting a new session).
Inspect the process tree: the old daemon and its MCP children are still alive next to the new daemon and its own fresh MCP children.
Observed today on macOS arm64:
Old daemon PID 79746 opencode2 serve --service, RSS ~700-780 MB, still running after the restart
Its children still alive: 2x npm exec @playwright/mcp --browser chromium and 1x npm exec @llmindset/hf-mcp-server, ~85-90 MB each
New daemon PID 80086 with its own set of MCP servers
Net result: two Playwright MCP instances running concurrently, ~1.5 GB held by the stale tree
Expected Behavior
service restart (and service stop) should terminate the previous daemon's entire process tree, including its MCP children, before starting the new one.
Actual Behavior
The old daemon and all its MCP children keep running after the restart, duplicating MCP servers and leaking memory. The stale tree could only be cleaned manually (kill -9 on the old daemon), and it took its MCP children down with it.
Additional Context
Combined with the daemon's high idle RSS (see related perf issue), opencode2 processes peaked at ~2 GB RSS on a 16 GB MacBook Air. The duplication happened on a single restart, so it looks consistent rather than intermittent. Related: an earlier service restart had also left the previous daemon's MCP servers behind, which is how multiple Playwright instances accumulated.
If your issue adds something distinct (specifically the stale process tree and MCP children surviving after a nominally-successful restart on macOS arm64), it may be worth keeping open and cross-linking to those.
Reproduced on 0.0.0-beta-17498 on macOS arm64. This also affects service stop, not only service restart.
The setup uses separate XDG homes and fixed ports for isolated services, then execs the underlying opencode2 command.
Observed sequence:
Service PID 29234 owned its configured listener.
Ran opencode service stop.
The listener closed, but PID 29234 remained alive in Ss state and was reparented to PID 1.
The process still held opencode-next.db, opencode-next.db-wal, and opencode-next.db-shm open.
Starting the service created PID 93758, which opened the same database and assumed the configured listener.
At that point the machine had 18 serve --service processes but only three listeners. The stale processes covered multiple isolated XDG homes and included both 0.0.0-next-17444 and 0.0.0-beta-17498.
Sending SIGTERM to the 15 confirmed non-listening processes did not terminate any of them. Some changed from Ss to Rs, but all remained alive. SIGKILL was required.
After removing the stale processes:
Exactly three expected service processes remained.
Each owned one expected listener.
All active MCP connections were healthy.
Active local MCP process trees remained attached to the current service.
There was also a correctness impact beyond memory usage. Two remote MCP servers began failing OAuth refresh with:
Refresh token is invalid or has already been claimed by another client.
Removing the stale credentials, authenticating sequentially, and killing the non-listening daemons restored both MCPs. I cannot prove the stale daemons caused the token claim race, but they were concurrently holding the same database and MCP state when it occurred.
This appears to be the same lifecycle bug reported here, still present in beta-17498. The stop path should verify that the old daemon and its process tree have exited before returning success.
Activity
github-actions commentedon Aug 13, 2026
This issue might be a duplicate of existing issues. Please check:
service restartcan silently fail to stop the incumbent and reuse the old process — same root gap: the restart does not verify the old daemon actually exited before starting a new oneservice restartspawnsserve --servicein a silent retry loop while the old process tree is still alive, requiring manual kills — closely related restart/process-cleanup failureIf your issue adds something distinct (specifically the stale process tree and MCP children surviving after a nominally-successful restart on macOS arm64), it may be worth keeping open and cross-linking to those.
lucazz commentedon Aug 18, 2026
Reproduced on
0.0.0-beta-17498on macOS arm64. This also affectsservice stop, not onlyservice restart.The setup uses separate XDG homes and fixed ports for isolated services, then
execs the underlyingopencode2command.Observed sequence:
29234owned its configured listener.opencode service stop.29234remained alive inSsstate and was reparented to PID 1.opencode-next.db,opencode-next.db-wal, andopencode-next.db-shmopen.93758, which opened the same database and assumed the configured listener.At that point the machine had 18
serve --serviceprocesses but only three listeners. The stale processes covered multiple isolated XDG homes and included both0.0.0-next-17444and0.0.0-beta-17498.Sending
SIGTERMto the 15 confirmed non-listening processes did not terminate any of them. Some changed fromSstoRs, but all remained alive.SIGKILLwas required.After removing the stale processes:
There was also a correctness impact beyond memory usage. Two remote MCP servers began failing OAuth refresh with:
Removing the stale credentials, authenticating sequentially, and killing the non-listening daemons restored both MCPs. I cannot prove the stale daemons caused the token claim race, but they were concurrently holding the same database and MCP state when it occurred.
This appears to be the same lifecycle bug reported here, still present in
beta-17498. The stop path should verify that the old daemon and its process tree have exited before returning success.github-actions commentedon Oct 18, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.