Skip to content

After the restart, the old instance runs in the background #52638

Closed

Description

@aaurelions

Description

It appears that the agent's loop isn't stopping.

Plugins

No response

OpenCode version

2.0.21

Steps to reproduce

  1. Start a long-running task on the agent
  2. Open a new window and run opencode upgrade
  3. In the first window, the client restarts, but the agent continues to run in the background
  4. Two competing agents are running simultaneously

Screenshot and/or share link

Image

Operating System

macOS

Terminal

Warp

Activity

  1. github-actions commented on Oct 2, 2026

    @github-actions
    Contributor

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

  2. aron-intframe commented on Oct 2, 2026

    @aron-intframe

    The agent loop is not in the client. In 2.0 the turn runs in the detached background server the client spawns (spawn(process.execPath, [..., "serve", "--register"], { detached: true, stdio: "ignore" }).unref(), packages/cli/src/services/daemon.ts:122), so whatever restarted the TUI in window 1 could not have stopped it. The thing that is supposed to stop it is Daemon.stopProcess (daemon.ts:91), and on a version change it can skip the kill: both escalation gates are /health calls with a 2s timeout (healthy(), daemon.ts:66-73), not pid checks. SIGTERM goes out at :95, the wait is 100x50ms, then :101-102 re-runs healthy() and returns early when it fails, so the SIGKILL at :104 never reaches a server that has already stopped answering - which is the state a wedged graceful shutdown is in (#37521: signal delivered, consumed, nothing happens). start() spawns the replacement regardless, because stopProcess is Effect.ignored at :115.

    I transcribed that ladder (health 2s -> SIGTERM -> 100x50ms -> re-health -> SIGKILL -> spawn + register) into a standalone Node script, since I can't build the 2.0 binary here, and drove two real processes with it - the old one wedged in graceful shutdown on an in-flight turn:

      stopProcess: SIGTERM sent to 2827368
    [daemon 2.0.20 pid=2827368] SIGTERM received: closing listener, awaiting in-flight agent turn
      stopProcess: pre-SIGKILL health gate failed -> return, no SIGKILL
    start: spawned replacement daemon pid= 2830340
      old daemon pid 2827368 alive: true
      SIGTERM sent: true | SIGKILL sent: false
      -> live agent-capable servers: 2
    

    The control run, with an old server that exits on SIGTERM, ends at live agent-capable servers: 1, so the gate is what makes the difference and not the harness. Two follow-ons from the same run: the orphan's own watchdog (daemon.ts:173-178) is SIGTERM-only with no escalation, so it cannot clear itself either (it self-signals at the 10s mark and stays up); and the replacement's register() (:166-172) overwrites server.json, so opencode service stop|status only ever sees the new pid and the orphan is unreachable from the CLI.

    Until this is fixed: run opencode service stop before upgrading, and clear an existing orphan with pgrep -af "serve --register" plus kill -9 on the pid that is not the one in ~/.local/state/opencode/server.json.

    For a fix, keying the escalation on process.kill(pid, 0) liveness instead of /health would cover it - the wedged server is by definition the one that stops answering - and letting start() surface the failure instead of Effect.ignore would stop a second daemon from being started silently.

    A cleanup step whose failure is swallowed so a duplicate starts quietly is a class we hit a lot; the checks we use for it are in our repo (ours): https://github.com/aron-intframe/agent-guards

  3. neriousy commented on Oct 20, 2026

    @neriousy
    Member

    Thanks for reporting this! In 2.0.23, service replacement was fixed to wait for the old process to exit and forcibly stop it if necessary, rather than leaving it running alongside its replacement. Update with opencode upgrade or npm i -g @opencode/cli@latest (desktop: update the app). Please comment or reopen if you still see competing instances on that version or newer.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions