Skip to content

[Bug] Managed OpenCode restart silently strands active sessions #2943

Closed

Description

@sum117

What happened

OpenChamber Desktop on Windows supervises a managed OpenCode server. When that server becomes unhealthy while sessions are busy, OpenChamber eventually restarts it successfully, but every in-flight turn is silently stranded. The UI provides no terminal error, interrupted state, or retry action. The affected sessions only become usable after another user message.

This happened twice in one day and has occurred repeatedly before.

Environment

  • OpenChamber: 1.18.4 (latest release)
  • Bundled OpenCode: 1.18.18 (latest release)
  • OS: Windows 11
  • CPU: AMD Ryzen 9 9950X3D, 32 logical processors
  • RAM: 61.7 GiB
  • Commit limit: 111.7 GiB

Reproduction shape

  1. Run two or three concurrent long-lived agent sessions, particularly sessions producing large repository diffs or tool output.
  2. The managed OpenCode health endpoint stops responding.
  3. OpenChamber counts health failures while the sessions remain marked busy.
  4. After the failure threshold and two-minute stale-busy grace, OpenChamber force-restarts OpenCode.
  5. The new OpenCode process becomes healthy, but interrupted turns do not resume or transition to a visible aborted/error state.

The trigger is made more likely by the OpenCode summary-diff amplification tracked in anomalyco/opencode#42748, but restart recovery and diagnostics remain OpenChamber concerns.

Incident 1 (OpenChamber local time, UTC-03)

16:18:37 [lifecycle] periodic health check failed (1/20)
16:23:11 [proxy] connect ECONNREFUSED 127.0.0.1:<port>
16:26:23 [lifecycle] periodic health check failed (28/20)
16:26:23 [lifecycle] OpenCode unhealthy with 3 busy session(s) for > 2 min - forcing restart
16:26:28 [OpenCode] Launching managed server on a new port
16:26:29 Detected OpenCode port

The interrupted turn had no continuation until a new user interaction approximately 39 minutes later.

Incident 2

17:44:10 [lifecycle] periodic health check failed (1/20)
17:48:41 [proxy] connect ECONNREFUSED 127.0.0.1:<port>
17:51:50 [lifecycle] periodic health check failed (28/20)
17:51:50 [lifecycle] OpenCode unhealthy with 3 busy session(s) for > 2 min - forcing restart
17:51:50 [OpenCode] Launching managed server on a new port
17:51:51 Detected OpenCode port

Again, no continuation or visible terminal state appeared until a later user message. Windows event logs contained no sleep, shutdown, or application-crash event for either interval. OpenChamber itself stayed running.

Aggregate-only storage evidence

A read-only audit of the healthy OpenCode database found:

  • Database: 2.016 GiB; PRAGMA quick_check: ok; freelist pages: 0
  • 26 sessions, 1,816 messages, 12,164 parts, 48,814 events
  • message.updated.1: 1,596,501,940 bytes
  • info.summary.diffs inside those events: 1,592,917,364 bytes (99.8%)
  • message.part.updated.1: 388,949,497 bytes
  • One message: 81 updates, 736.4 MiB total, 11.4 MiB maximum event
  • Two active session aggregates: 998.5 MiB and 761.2 MiB

No payload content, identifiers, titles, or paths were inspected for these measurements. This is consistent with anomalyco/opencode#42748 and explains the server pressure, but not the silent post-restart UX.

Expected behavior

After a forced managed-server restart, OpenChamber should:

  1. Reconcile sessions that were busy before restart into an explicit interrupted/aborted state.
  2. Refresh their status and messages after rebinding the event stream.
  3. Show a persistent notification with retry/resend rather than silently leaving the turn unfinished.
  4. Record the health failure class (timeout, refused connection, invalid response), managed child exit code/signal, and runtime stderr so the initiating server failure can be diagnosed.

The current onOpenCodeRestarted event-stream rebind restores connectivity but does not make interrupted turns recoverable or visible.

Activity

  1. added
    bugSomething isn't working
    area:sessionsSession lifecycle, list, status, history
    priority:highMaintainer-set only: blocks core workflows, data loss, or many users
    on Aug 15, 2026
  2. openchamber-bot commented on Aug 15, 2026

    @openchamber-bot
    Contributor

    Thanks for the thorough report, @sum117 — you've mapped the failure path clearly: health failures accumulate while sessions stay busy, the stale-busy grace expires, OpenCode is force-restarted, the event stream rebinds (onOpenCodeRestarted), but in-flight turns stay silently stranded with no aborted/interrupted state, notification, or retry action.

    Triaged as a bug in the sessions area (session lifecycle/status reconciliation after a forced managed-server restart) at high priority: it silently discards in-flight agent work in a core workflow, and it has recurred for you.

    To help pin down root cause and repro:

    1. Do you have any managed OpenCode stderr captured around the first health-check failure (the window before the first [lifecycle] log)? That would confirm whether the process died, hung, or kept running but unresponsive before the forced restart.
    2. Have you seen this only on Desktop, or also with the CLI serve/web runtime? The managed lifecycle is shared server-side, so this decides whether the fix belongs in the web runtime or the Electron shell.
    3. While a session is stranded, what does the UI show — a continuing spinner, the last assistant message, or nothing at all? And does a new message always unstick it, or does a page refresh sometimes help?

    Even just one incident window of logs would help. Thanks again for the detailed evidence.

  3. sum117 commented on Aug 15, 2026

    @sum117
    Author

    Thanks. Answers below.

    1. Managed OpenCode stderr / process state

    No post-start managed stderr is available. In createManagedOpenCodeServerProcess, OpenChamber attaches stdout/stderr listeners only while waiting for the opencode server listening line; finish() removes both listeners once startup succeeds, and the returned process wrapper does not persist runtime stderr. The separate OpenCode log contains no fatal/OOM entry at either boundary.

    The lifecycle evidence points to a live-but-unhealthy child rather than a clean observed exit:

    • The monitor did not take the OpenCode process exited, restarting... branch. It counted failures through 28/20, which means isManagedOpenCodeProcessAlive() continued to return true.
    • Requests first failed with socket hang up, then with ECONNREFUSED.
    • Incident 1: shutdown was followed by Timed out waiting for OpenCode port <port> to be released before a new port was allocated.
    • Incident 2: the old child/port released quickly and the replacement started immediately.
    • Windows Application/System event logs had no matching process crash, sleep, or shutdown event.

    So the strongest conclusion is: the managed child stayed present while its server became unresponsive/stopped listening; the exact initiating exception is unavailable because runtime child stderr/exit diagnostics are not retained.

    2. Runtime

    Observed only in OpenChamber Desktop on Windows so far. I have not run a controlled reproduction using standalone opencode serve or OpenChamber web. Desktop was launching the bundled child as:

    opencode.exe serve --hostname 127.0.0.1 --port <allocated-port>
    

    Therefore the OpenCode server wedge may reproduce standalone, but the silent forced-restart recovery behavior is confirmed only through the Desktop-managed lifecycle.

    3. Stranded-session UI behavior

    The observable behavior in both incidents was:

    • The active assistant turn stopped producing output with no error, interrupted marker, or retry action.
    • OpenChamber still counted three sessions as busy while deciding whether to restart.
    • After the replacement server became healthy, the interrupted turn did not continue or acquire a visible terminal result.
    • Sending a new user message in the same session started a new assistant turn and made the session usable again in both incidents.
    • A page refresh was not attempted, so I cannot say whether refresh alone repairs the presentation.
    • I did not capture a screenshot and cannot reliably distinguish whether the spinner remained visible after the restart versus the UI simply showing the incomplete last assistant turn. The reliable symptom is silent non-completion with no surfaced recovery state.

    OpenCode logs show the old run ending with an active loop and the replacement run booting under a new run ID, with no continuation of the interrupted message. I can capture UI state plus process/runtime metrics on the next occurrence if that would help.

  4. added
    root-cause:foundBot traced a concrete code-level cause (not yet confirmed by the reporter)
    on Aug 15, 2026
  5. openchamber-bot commented on Aug 15, 2026

    @openchamber-bot
    Contributor

    Reproduced. I wired the session runtime and lifecycle runtime together exactly as packages/web/server/index.js does, drove the health-check failure threshold with 3 busy sessions, and confirmed that after the forced restart the busy sessions are silently stranded.

    Reproduction

    Test: packages/web/server/lib/opencode/reproduce-2943.test.js on branch reproduce/issue-2943 (passes — it asserts the buggy behavior).

    Steps reproduced:

    1. 3 sessions report session.status: busy (matching "OpenCode unhealthy with 3 busy session(s)").
    2. Health endpoint returns failure; 20 consecutive failures accumulate at the 15s interval ((1/20) → (20/20)).
    3. At threshold, shouldSkipRestartForBusySessions() holds for the 2-minute STALE_BUSY_GRACE_MS.
    4. Grace expires → restartOpenCode() runs: old process closed, replacement spawned, onOpenCodeRestarted fires (the rebindUpstream() rebind).

    After the restart, the following hold — the bug:

    • getActiveSessionCount() still returns 3 (all sessions remain busy in the session runtime).
    • getSessionStateSnapshot() / getSessionActivitySnapshot() still report all 3 as busy.
    • No terminal session.status idle/aborted/error event was broadcast.
    • No openchamber:notification was broadcast.
    • The UI therefore keeps the turn in a busy/streaming state with no error surface, no retry — matching the report.

    Root cause

    onOpenCodeRestarted (index.js:1090) only calls messageStreamRuntime.rebindUpstream(). The session runtime is never reconciled: resetAllSessionActivityToIdle() exists in session-runtime.js:284 (and is exported + documented) but is never invoked anywhere in production code — only in its own unit test. So after a forced restart, the stale busy phases/statuses survive indefinitely (until a later user message produces a new status transition), the active-session count stays elevated, and nothing emits a terminal event or notification for the interrupted turns.

    Additional gaps matching the issue's expected behavior (not reproduced in the test, verified by inspection):

    • No diagnostic recording of the health failure class (timeout/refused/invalid), managed child exit code/signal, or runtime stderr — restartOpenCode only sets state.lastOpenCodeError from start-failure messages.
    • killProcessOnPort is a no-op on Windows (lifecycle.js:57), so the restart is the only recovery path — making the missing session reconciliation more visible there.

    Likely fix location

    • packages/web/server/index.js onOpenCodeRestarted (or packages/web/server/lib/opencode/lifecycle.js restartOpenCode): reconcile sessions busy at restart time into an explicit interrupted/aborted state, emit terminal status, and broadcast a notification with retry/resend.
    • packages/web/server/lib/opencode/session-runtime.js: resetAllSessionActivityToIdle() is the existing-but-unwired primitive.
  6. added a commit that references this issue on Aug 19, 2026
    2db90f7
  7. added 3 commits that reference this issue on Aug 21, 2026
    472c08c
    57a0b1e
    7d285b1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:sessionsSession lifecycle, list, status, historybugSomething isn't workingpriority:highMaintainer-set only: blocks core workflows, data loss, or many usersroot-cause:foundBot traced a concrete code-level cause (not yet confirmed by the reporter)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions