Skip to content

service: restart leaves stale daemon and duplicated MCP servers running #42270

Closed as not planned

Description

@cioffiAI

Summary

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.

Environment

  • opencode version: 0.0.0-next-17403 (next channel, V2 beta)
  • OS: macOS 26.6.1 (Darwin 25.6.0, arm64)
  • Terminal: Apple_Terminal, TERM=xterm-256color
  • Shell: /bin/zsh
  • Install/channel: local binary in ~/.opencode/bin, channel next
  • Active plugins: ~/.config/opencode/plugins.v2/memory.ts (opencode-memory)

Reproduction

  1. Configure local MCP servers (e.g. npx -y @playwright/mcp --browser chromium and npx -y @llmindset/hf-mcp-server) in ~/.config/opencode/opencode.jsonc.
  2. Start a session (spawns the serve --service daemon plus the MCP children).
  3. Run opencode2 service restart (or service stop followed by starting a new session).
  4. 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.

Activity

  1. github-actions commented on Aug 13, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    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.

  2. lucazz commented on Aug 18, 2026

    @lucazz

    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:

    1. Service PID 29234 owned its configured listener.
    2. Ran opencode service stop.
    3. The listener closed, but PID 29234 remained alive in Ss state and was reparented to PID 1.
    4. The process still held opencode-next.db, opencode-next.db-wal, and opencode-next.db-shm open.
    5. 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.

  3. github-actions commented on Oct 18, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions