Skip to content

Suspended-session recovery never runs in opencode serve mode: background jobs stay running forever after a server death #62925

Open

Description

@rssitnt

What happened

After an unclean death of an opencode serve process, durable background jobs (subagent tasks, background shells) stay in running state forever, and the parent sessions keep waiting for tool results that already completed. Restarting the server does not clear them.

Root cause

Suspended-session recovery exists and is durable, but it is only wired for the managed service mode. In packages/server/src/process.ts (dev branch, checked 2026-11-09):

if (lifecycle) {
  yield* installRestartContinuity(Context.get(context, SessionRestart.Service)).pipe(...)
}
...
const installRestartContinuity = Effect.fnUntraced(function* (restart: SessionRestart.Interface) {
  yield* Effect.forkScoped(restart.resumeSuspendedSessions)
})

opencode serve boots with no lifecycle, so resumeSuspendedSessions never runs in this mode: pending background rows are never swept, subagent results are never delivered, and shell rows are never released.

Real-world evidence

A long-lived opencode serve deployment that survived several crash/restart cycles accumulated 44 running job rows aged 36.7–138.9 hours, all older than the then-current server process (proving no server had recovered them). 33 were subagent jobs whose child sessions had already completed — their results were never delivered, so the parents sat indefinitely in "waiting for tool result". A deliberate clean restart did not clear the rows.

Reproduction sketch

  1. Start opencode serve.
  2. Run a turn that spawns a subagent (or a background shell task).
  3. Kill the server process (e.g. kill -9) before the job completes or right after the child finishes.
  4. Restart opencode serve.
  5. The job row is still running; the parent session still waits for the tool output even though the child finished.

Expected

At-least-once recovery of suspended sessions should cover serve mode too. The codebase already makes this durability argument for schedules (// Schedules fire from any server process (service, opencode serve, or --standalone); the table holds all their state.) — the same reasoning applies to execution claims and pending background jobs.

Suggested fix

Fork the same sweep on the non-lifecycle boot path (call installRestartContinuity for serve mode as well), keeping the existing per-session resume budget as the runaway guard. If multiple servers can share one storage root, the sweep may additionally need claim takeover coordination; even without it, wiring serve-mode recovery matches what the managed mode already does.

Notes

As an interim workaround we run an external sweeper over the official HTTP APIs (delivering the synthetic completion notification ourselves; duplicates are safely rejected as 409 Conflict, which also makes an upstream fix idempotent with such a sweeper). The primitives are already there — the only missing piece is calling the sweep in serve mode.

Activity

  1. opencodejr commented on Nov 9, 2026

    @opencodejr

    Thanks for the detailed report and the deployment data, @rssitnt.

    I think this is a duplicate of #56313. It reports the same root cause: installRestartContinuity in packages/server/src/process.ts only runs when a lifecycle is passed, which happens only in service mode. So plain opencode serve never sweeps suspended sessions, and background subagent and shell jobs stay running with no completion delivered to the parent. That thread already discusses the gate and how to fix it, and has picked up another duplicate (#62488).

    Possible duplicates and closely related issues, ordered from most to least likely:

    If you add your evidence (44 rows, 36.7 to 138.9 hours old, a clean restart not clearing them) and your idempotent 409 sweeper workaround to #56313, it will help there.

    The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions