Skip to content

[Windows][OpenCode 2] MC 0.44.0 hangs opencode serve at boot: /health never answers, supervisor kill/restart loop #554

Open

Description

@morpheus9393

Short description

On Windows, MC 0.44.0 hangs opencode serve at boot: the process stays alive but /health never answers, so an external supervisor kills it after ~20 failed health checks and restarts it — endless restart loop. Pinning @0.43.2 boots cleanly on the same machine and binary, so the regression is in 0.44.0.

What happened?

Environment: OpenChamber (Electron app) manages a headless opencode server: opencode.exe serve --hostname 127.0.0.1 --port <p> (opencode v2.0.18, Windows 11 x64). OpenChamber health-checks http://127.0.0.1:<p>/health every 15 s and kills + restarts the server after ~20 consecutive timeouts.

Timeline (2026-09-28, local time UTC+2):

  • 13:54 — @cortexkit/opencode-magic-context@0.44.0 published to npm (registry: 2026-09-28T11:54:58Z).
  • 14:02 / 14:09 — servers still booted normally (MC RPC listening, commands registered).
  • 14:21 — first hung boot. MC log: SQLite boot busy timeout: backend=Bun timeout=5000ms, then a cascade of another Magic Context RPC server is active for this project (pid …, port …); starting separate instance on a new port — several new listeners within the same second, from different pids (stale instances of previous kill/restart cycles probing each other).
  • 14:23 onward — supervisor log fills with periodic health check failed (1/20) class=timeout … (18/20), then process exited, restarting..., and the pattern repeats. 17 restarts over ~5 hours.
  • The stderr of the hung processes shows MC itself loads (including the protected_tags is deprecated and ignored warning) — the plugin gets that far, then the boot stalls. The process never crashes and never prints an error.
  • 19:09 — plugin entry commented out as a rescue → server boots and stays healthy (without MC).
  • 19:37 — pinned @cortexkit/opencode-magic-context@0.43.2 in the global config and re-enabled → boots cleanly, /health returns HTTP 200, no further restarts. Also verified in an isolated opencode serve on a test port with the same bundled binary: 0.43.2 loads, single RPC listener, /health 200 within ~2 s.

Expected: opencode serve with MC 0.44.0 boots and answers /health within seconds, like 0.43.2 does.

Observed: process alive (no crash, no exit code, no error output) but HTTP never comes up; only the supervisor's kill breaks the state — and then the next boot hangs the same way.

Repro

  1. Windows 11 x64, opencode v2.0.18, headless opencode serve.
  2. Global plugin config: "@cortexkit/opencode-magic-context@latest" (resolves 0.44.0).
  3. Start the server → process stays alive, /health times out indefinitely.
  4. Pin @0.43.2, restart → healthy in seconds.

Analysis (hypothesis, grounded in the 0.44.0 code path behind #553)

  • The migration guard in openDatabase/openDatabaseAsync calls inspectRpcServerDiscovery() + inspectLivePiProcesses(). On Windows these run powershell (CIM Win32_Process query, WINDOWS_CIM_PROBE_TIMEOUT_MS = 5_000) and tasklist via execFileSync — synchronous, event-loop-blocking, without windowsHide (the same spawn sites confirmed for [Windows][OpenCode 2] Magic Context repeatedly opens PowerShell and CMD windows that steal focus #553).
  • New in 0.44.0 (OpenCode V2: every turn refused in silence with 0.43.2 — v2 refuseIfUnsafe / "context storage is not durable" (outcome=interrupted, no assistant message) #546): v2/hooks/storage-gate.ts retries a refused DB open every 5 s (V2_STORAGE_REOPEN_INTERVAL_MS = 5_000), and each retry re-runs the synchronous Windows probes.
  • Under the supervisor's rapid kill/restart cycles, several MC instances probe each other (see the "another Magic Context RPC server is active" cascade — one new listener per attempt), and the synchronous probes block the event loop long enough that the HTTP server never gets to answer /health → kill → restart → loop.
  • The MC log shows the stall happens around DB open, before the v91 migration lane: upstream migration lane at boot: database=v90, supported_fence=v90 — the DB never reached fence 91 during the loop, which is also why downgrading to 0.43.2 was still possible here.

Happy to run doctor --issue non-interactively if there's a flag for it — the interactive wizard doesn't complete in a headless shell, so the diagnostics below are filled in manually per the template.

Diagnostics

  • Plugin version: 0.44.0 (npm dist-tags.latest, published 2026-09-28T11:54:58Z)
  • OpenCode version: 2.0.18 (bundled CLI of OpenChamber)
  • Platform: Windows 11 x64
  • Client: OpenChamber-managed headless opencode server (opencode serve)
  • Config: plugin spec @latest in global opencode.jsonc; protected_tags: 15 present. The deprecation warning appears on stderr in both healthy (0.43.2) and hung (0.44.0) boots, so it is not the cause.

Log output

Supervisor (OpenChamber) log — one full kill/restart cycle; this pattern repeats ~every 5 minutes for hours:

[14:23:55] [lifecycle] periodic health check failed (1/20) class=timeout
[16:50:12] [lifecycle] periodic health check failed (4/20) class=timeout
[16:50:27] [lifecycle] periodic health check failed (5/20) class=timeout
... (15 s interval)
[16:54:57] [lifecycle] periodic health check failed (18/20) class=timeout
[16:55:11] [lifecycle] periodic health check: OpenCode process exited, restarting...
[16:56:51] [lifecycle] periodic health check failed (1/20) class=timeout   ← loop restarts

MC log at the hung boot (sanitized; %TEMP%\opencode2\magic-context\magic-context.log):

[14:21:47] [magic-context] SQLite boot busy timeout: backend=Bun timeout=5000ms path=~\.local\share\cortexkit\magic-context\context.db
[14:21:47] [magic-context] upstream migration lane at boot: database=v90, supported_fence=v90
[14:21:47] [magic-context][global] models-dev-cache: API layer loaded 96 model metadata entries
[14:21:47] [magic-context] registered server-side commands: ctx-status ctx-recomp ctx-dream ctx-flush ctx-embed ctx-wrapup
[14:21:48] [rpc] another Magic Context RPC server is active for this project (pid 100136, port 49564); starting separate instance on a new port
[14:21:48] [rpc] server listening on 127.0.0.1:62756
[14:21:48] [rpc] another Magic Context RPC server is active for this project (pid 59276, port 60121); starting separate instance on a new port
[14:21:49] [rpc] server listening on 127.0.0.1:62766
[14:21:49] [rpc] another Magic Context RPC server is active for this project (pid 12976, port 57412); starting separate instance on a new port
[14:21:50] [rpc] server listening on 127.0.0.1:62801
... (one additional listener every ~400 ms, multiple pids from earlier restart cycles)

With @0.43.2 pinned, same machine, same binary: single RPC listener, /health HTTP 200 within ~2 s, zero health-check failures since.

Workaround

Pin @cortexkit/opencode-magic-context@0.43.2 and restart all hosts sharing the DB. Note for anyone hitting this: the stall happens before the v91 migration runs, so downgrading stays safe — but once 0.44.0 has completed the v91 migration on a DB, 0.43.2 will refuse it (fence 90 vs 91), so pin before the first successful 0.44.0 boot.

Activity

  1. magic-alfonso commented on Sep 28, 2026

    @magic-alfonso

    Thanks for the detailed report, and for trying the pin and the isolated check. We're investigating.

    Your analysis matches code that changed in 0.44.0: on Windows, the storage check runs powershell and tasklist synchronously, and 0.44.0 added a storage-open retry every 5 seconds that repeats those checks. The fix for #553 already hides those windows. For this issue we're making the checks asynchronous and bounded, and making sure the server always comes up no matter what state Magic Context's storage is in. We're reproducing it on a real OpenCode 2.0.18 host with Windows-like conditions first.

    One detail we'd like to understand: the log line you quote from the hung boot says supported_fence=v90, and 0.44.0 would print v91 there. So that line came from a 0.43.x build. OpenCode 2 keeps its cached @latest install until it's updated, so more than one plugin version may have been running across the restarts. If you still have the magic-context.log from a hung boot, could you share the lines that contain supported_fence, storage, or refus, with their timestamps? And do you know whether more than one opencode.exe or OpenChamber instance was running at the time?

    Your workaround (pin @0.43.2 before 0.44.x completes its first boot) is correct, and your note about the migration order is right too. Note that 0.44.1, released today, doesn't include a fix for this yet, so keep the pin until we post here.

  2. morpheus9393 commented on Sep 29, 2026

    @morpheus9393
    Author

    Thanks for the pointer — you were right to question the version, and after re-checking I owe you a correction: 0.44.0 was never installed on this host. The hung processes were running 0.43.2 all along.

    Cache proof that @latest never picked up 0.44.0

    The OpenCode package cache has exactly one Magic Context install root (...\packages\@cortexkit\opencode-magic-context@latest), untouched since 2026-09-25 14:45 local — three days before the 0.44.0 publish:

    package.json (install root, mtime 2026-09-25 14:45:26):
      { "dependencies": { "@cortexkit/opencode-magic-context": "0.43.2" } }
    
    node_modules\@cortexkit\opencode-magic-context\package.json → version: 0.43.2
      package.json mtime: 2026-09-25 14:45:20
      dist mtime:      2026-09-25 14:45:24
    

    No second install directory exists, and nothing in it was modified on 2026-09-28. So on this host the @latest spec resolved once (→ 0.43.2, on 09-25) and never re-resolved to 0.44.0 after the publish at 2026-09-28 09:54Z — consistent with the "cache resolves @latest exactly once and never refreshes" behavior we had already observed on this setup. My original report's causal chain (publish → re-resolve → 0.44.0 boots → hang) did not happen.

    Requested magic-context.log lines (supported_fence / storage / refus)

    All supported_fence lines in the log, ever (the log starts 2026-09-25), show database=v90, supported_fence=v90. There is no v91 line anywhere, including inside the hang windows on 09-28. Lines during/around the hang period (UTC):

    [2026-09-28T12:38:55.731Z] [magic-context] SQLite boot busy timeout: backend=Bun timeout=5000ms path=~\.local\share\cortexkit\magic-context\context.db
    [2026-09-28T12:38:55.813Z] [magic-context] upstream migration lane at boot: database=v90, supported_fence=v90
    [2026-09-28T14:21:47.672Z] [magic-context] SQLite boot busy timeout: backend=Bun timeout=5000ms path=~\.local\share\cortexkit\magic-context\context.db
    [2026-09-28T14:21:47.710Z] [magic-context] upstream migration lane at boot: database=v90, supported_fence=v90
    [2026-09-28T17:37:01.303Z] [magic-context] upstream migration lane at boot: database=v90, supported_fence=v90
    [2026-09-28T18:19:52.097Z] [magic-context] upstream migration lane at boot: database=v90, supported_fence=v90
    [2026-09-28T18:27:01.648Z] [magic-context] upstream migration lane at boot: database=v90, supported_fence=v90
    [2026-09-28T19:34:44.109Z] [magic-context] upstream migration lane at boot: database=v90, supported_fence=v90
    

    There are no storage-refus* lines at all in the log (the only refus hits are unrelated dreamer curation-safety refusals). So the DB never got near a migration, and no storage gate ever refused — as you predicted, the v90 line I originally quoted was from a 0.43.x build; in fact every line was.

    Context on the SQLite boot busy timeout lines: they are chronic on this host, not specific to the hang day — 6 on 09-25, 15 on 09-26, 7 on 09-27, 6 on 09-28, i.e. roughly once per boot on calm days too, and the boot proceeds normally after them.

    Your multi-instance question: yes, many

    Every new instance that shares a project logs the RPC cascade. Distinct opencode.exe PIDs seen in that cascade on 09-28 alone: 19232, 100136, 16352, 44096, 104912, 100880, 12976, 44144, 100184, 102904, 59276 (11+ concurrent hosts on a typical day — one OpenChamber-managed server plus several user-launched session hosts). Some persisted across the whole day. Example from a boot inside the hang window:

    [2026-09-28T14:21:48.181Z] [rpc] another Magic Context RPC server is active for this project (pid 100136, port 49564); starting separate instance on a new port
    [2026-09-28T14:21:48.183Z] [rpc] server listening on 127.0.0.1:62756
    

    What the supervisor log actually shows

    Daily count of periodic health check failed lines in the OpenChamber supervisor log:

    date health-fail lines
    09-22 2
    09-23 4
    09-24 341
    09-25 463
    09-26 45
    09-27 13
    09-28 418
    09-29 1 (self-recovered)

    So the kill/restart storm is recurring and version-independent — it also happened on 09-24/09-25 with pre-0.43.2 builds, was calm on 09-26/09-27 with the same 0.43.2, and stormed again on 09-28 with the same 0.43.2. On 09-28 the first health failure followed a sleep/wake signature by 4 seconds:

    [2026-09-28 14:23:51.313] [error] [electron] autoUpdater error Error: net::ERR_NETWORK_IO_SUSPENDED
    [2026-09-28 14:23:55.701] [warn]  [lifecycle] periodic health check failed (1/20) class=timeout
    ...
    [2026-09-28 19:00:01.602] [info]  [lifecycle] periodic health check failure threshold reached, restarting OpenCode...
    

    Restart diagnostics from a hung instance (process alive, /health never answering; stderr tail trimmed to the Magic Context line — full tail also contains unrelated plugin warnings):

    [2026-09-28 17:14:50.206] [warn] [lifecycle] managed OpenCode restart diagnostics {
      reason: 'periodic-health-failure',
      healthFailure: { class: 'timeout', detail: 'TimeoutError: The operation was aborted due to timeout', source: 'periodic' },
      process: { pid: 36220, exitCode: null, signalCode: null,
                 stderrTail: '...\n[magic-context] protected_tags is deprecated and ignored; use protected_tokens instead.\n',
                 alive: true },
      busySessionCount: 0 }
    

    (net::ERR_NETWORK_IO_SUSPENDED occurs ~3x/day here on every day including calm ones, so it alone isn't the trigger, but the 4-second proximity to the storm onset on 09-28 is notable.)

    Revised picture

    Given all of the above, the original attribution in this issue — "0.44.0's new synchronous storage probes hang the boot" — does not hold for this host: the storm predates 0.44.0's publish on 09-24/09-25, and the 09-28 storm ran entirely on 0.43.2. What we can say from the evidence: the managed opencode serve process stops answering /health (event-loop-level block or deadlock, process alive with exitCode null) with Magic Context loaded, under an environment with many concurrent Magic-Context-hosting instances and a contended context.db, and it has happened across multiple MC versions. Whether Magic Context is causal (e.g., a boot path that blocks on the busy DB/lock while 10+ hosts contend) or merely present, we can't tell from these logs alone.

    Two questions from our side:

    1. In 0.43.2, is there any boot/init path that could block the host's event loop for minutes when context.db is contended by many concurrent hosts (e.g. the 5000ms busy timeout path retrying synchronously, or the RPC single-instance probe)?
    2. If OpenCode's @latest cache genuinely never re-resolves (our observation, twice now), the upgrade path to the fixed 0.44.x will be "delete the @latest install root or change the spec to an explicit pin" — might be worth calling out in the release notes for [Windows][OpenCode 2] Magic Context repeatedly opens PowerShell and CMD windows that steal focus #553/[Windows][OpenCode 2] MC 0.44.0 hangs opencode serve at boot: /health never answers, supervisor kill/restart loop #554-adjacent fixes.

    For now we're staying pinned to @0.43.2 (still working; the 09-28 storm ended after the pin plus removal of stale instances, though per the above we can't attribute the recovery to the pin specifically). Happy to run instrumented tests on an isolated port with a fixed build if that helps.

  3. added a commit that references this issue on Sep 29, 2026
  4. magic-alfonso commented on Sep 29, 2026

    @magic-alfonso

    0.44.2 is now on npm with the part of this we could reproduce: OpenCode 2 keeps answering /health while Magic Context's storage opens. The process checks before opening the database are asynchronous and time-limited, so a supervisor no longer sees the server stall at boot.

    The restart storms you described on 0.43.2, with many OpenCode instances sharing one database, are still under investigation, so I'm leaving this open. If you're pinned to 0.43.2: 0.44.2 migrates a v90 database to v91 on first start, after which 0.43.x refuses it, so upgrade every host that shares the database.

  5. magic-alfonso commented on Sep 29, 2026

    @magic-alfonso

    0.44.3 includes the refined busy-database handling. A turn now waits up to 250 ms for a lock held by another OpenCode instance, and background writes give up after 25 ms and retry on their next run, so a brief lock no longer refuses turns and a long one no longer stalls the host. With 11 OpenCode 2 instances sharing one context.db under a 10-second held write lock, every /health probe was answered. Leaving this open until you can confirm whether the restart loops you saw stop on 0.44.3.

  6. magic-alfonso commented on Oct 17, 2026

    @magic-alfonso

    Checking in: are you still seeing the /health restart loops on 0.49.10 with several OpenCode instances sharing one database? Since 0.44.3, a turn waits briefly for a lock held by another instance and background writes back off, and 0.49.6 extended that wait to 2 seconds. If the loops have stopped, we'll close this.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions