Repository navigation
Instance bootstrap hangs forever (Linux): spawned git child exits but is never reaped — TUI renders, but Enter can never start a session #41806
Description
Summary
On Linux, opencode (TUI) intermittently hangs forever during instance bootstrap: a git child spawned during init exits but is never reaped (it stays <defunct> for the life of the process), the bootstrap await never settles, and the window renders normally — accepts typing — but Enter can never start a session because the server side never finished booting. No error is shown or logged; the process idles in epoll indefinitely.
Environment
- opencode 1.18.15, Linux 6.8.0-124-generic (Ubuntu), TUI launched in a terminal
- Heavily concurrent host: multiple opencode instances (interactive +
opencode run) active in the same projects
Evidence (from two live hung instances, same day)
Log (shared ~/.local/share/opencode/log/opencode.log) — the hung instance run stops mid-bootstrap and never logs another line:
Instance A stopped at init:
16:33:51.880 bootstrapping directory=/home/oren/projects/kk/billie
16:33:52.020 message="all LSPs are disabled"
16:33:52.021 message="all formatters are disabled"
16:33:52.021 message=init
<- nothing, ever again (14 lines total, 0 sessions)
Instance B stopped one phase later:
16:40:13.397 message=init
16:40:16.163 message="watcher backend" ... backend=inotify
16:40:16.165 message="project copy refresh started" projectID=1d4fe22...
<- never "done", nothing again (0 sessions)
A healthy boot of the SAME project in the SAME minute continues: init → watcher backend → project copy refresh started/done → init count=281 → sessions created on Enter.
Process table — the discriminator. Each hung instance holds exactly one unreaped git child; seven healthy same-host instances hold zero:
$ ps --ppid <hung-tui-pid> -o pid,stat,args
3377914 Sl+ python3 .../mcp_server.py (local MCP, alive, fine)
3378581 Zs [git] <defunct> (spawned during bootstrap, never reaped)
The zombies' spawn times match the last log line to the second (e.g. git spawned 16:40:16 local; project copy refresh started logged 16:40:16.165Z). Parent state: wchan=ep_poll, i.e. the event loop is alive and waiting — an await whose completion event was lost, not a deadlock on a sync call.
Trigger correlation
Both hangs happened when the window was opened into a project where other opencode instances were concurrently running git against the same shared snapshot store (~/.local/share/opencode/snapshot/<projectID>/ — visible as concurrent tracking hash=... log lines from the other instances). It is a race: a boot into the same busy project 90 s earlier survived. Historical scan of our log: ~30 instance runs since 2026-08-07 (of 895) logged creating instance but never reached watcher backend, clustered in bursts of concurrent activity.
What it is not
- Not input/keybind/TTY related — termios verified sane; the failure is server-side, before any input matters.
- Not plugins spawning the git — our plugins only spawn git synchronously (
execFileSync, which reaps its own child). The unreaped child is core's. - Not resolved by any key — only closing and reopening the window.
Expected
Bootstrap either completes or fails loudly. A spawned git child's exit should always settle the awaiting promise (success or error), under any interleaving — including exiting very fast, or failing on index.lock contention from a sibling instance.
Suspected area
The boot-time snapshot/project-copy git spawn: child exit (possibly an immediate/fast exit, or a nonzero exit on lock contention) is not observed — either the exit handler is registered after the exit event has already fired (Bun subprocess reaping race) or an error path never rejects the awaited promise. Happy to run an instrumented build or provide more process-level captures; reproduction should be possible by booting a TUI into a project while a loop hammers git operations against that project's snapshot dir.
Activity
github-actions commentedon Aug 11, 2026
This issue might be a duplicate of an existing issue. Please check:
[git] <defunct>zombie children during instance bootstrap that are never reaped, event loop stuck inepoll/ep_poll, log stops mid-bootstrap, no error surfaced. The prior report is WSL2 + concurrent Desktop bootstraps; this report is native Linux + concurrentopencode runinstances against the same project snapshot store. The underlying race (child exit not observed, awaiting promise never settles) appears identical.martialebeau commentedon Aug 12, 2026
Corroboration — Windows, 4 incidents in 6 days, context-size correlation\n\nAdding evidence from a different platform (Windows, opencode 1.18.16) that confirms this bug and identifies a trigger amplifier: opening context window size.\n\n### Environment\n- opencode 1.18.16, Windows 11, local NTFS drive (
X:\\), TUI launched in terminal\n- Multiple concurrent opencode instances (3+ terminals) sharing the same working directory\n- One local MCP server registered (llm-router, Python stdio)\n\n### 4 zombie incidents (all same fingerprint: CPU spin 40-136%, zero network, zero disk writes, zero log output, 800MB-4.5GB RAM)\n\n| PID | Date | CPU | Duration | RAM | Trigger context |\n|---|---|---|---|---|---|\n| 39476 | 08-05 → 08-08 | ~136% | 3 days | 4.5 GB | MCP error-32601→ infinite retry (#41848) |\n| 36140 | 08-08 18:11 | ~70% | 188 min | — | Concurrent instances during close protocol |\n| 38504 | 08-11 18:31 | 63% | 196 min | 1.0 GB | 3 concurrent instances, git snapshot contention |\n| 13916 | 08-11 19:11 | 41% | 38 min | 888 MB | Same — TUI kept rendering raw ANSI SGR codes as literal text |\n\n### The context-size trigger correlation\n\nWe traced the root cause chain through our own incident investigation. The timing correlates exactly with harness modifications that increased the opening context window:\n\n- Before 08-03: ~15-18K opening context tokens — zero zombie incidents (months of stable operation)\n- 08-03 to 08-11: Incremental harness changes (new boot steps, state file growth) pushed opening context to ~43K tokens — 4 zombie incidents in 6 days\n- The larger context means: (a) longer API streams → higher probability of mid-stream errors → triggers #41848 unbounded retry; (b) more file writes per turn → more git snapshot spawns → higher probability of #41806 exit-handler race\n\n### The two bugs interact\n\n#41848 (unbounded retry) and #41806 (git exit-handler race) are not independent — they form a failure cascade:\n1. Large context → longer API stream → stream error → #41848 infinite retry\n2. Retry loop produces repeated tool calls → each writes files → each triggers a git snapshot\n3. Multiple concurrent instances →index.lockcontention on shared snapshot store\n4. Git child exits on lock contention before exit handler registered → #41806 → server epoll stuck forever\n5. TUI client (separate process) keeps rendering → phantom spinner + locked input + ANSI flood\n\n### Mitigation (our workaround)\n- External watchdog (PowerShell scheduled task): samplesopencode.exeCPU every 10 min, kills any process with double-sampled CPU ≥ 40% AND age \u003e 5 min. Logs health lines for post-mortem.\n- Context reduction (just applied): reduced opening context from ~43K to ~28K tokens by slimming instruction files and consolidating boot commands. Hypothesis: this reduces trigger probability for both bugs.\n- Behavioral: avoiding concurrent opencode instances against the same working directory when possible.\n\n### Key observations\n- Thewchan=ep_pollfinding from the original report matches our Windows evidence: the process is alive, consuming CPU, but the event loop is stuck — no I/O, no timers, no log output. The TUI client keeps rendering faithfully.\n- The ANSI flood symptom (raw[555;NN;32Mcodes rendered as literal text) is a secondary symptom of the same server-stuck state: the TUI render loop is running but receiving no updates from the dead server, so it re-renders stale state with malformed color codes.\n- The bug is worse on Windows because process lifecycle is different — headless servers (--port) survive session close and keep spinning indefinitely.\n\nHappy to provide Windows-specific process dumps, WAL file evidence (596MB WAL from runaway DB writes during one zombie incident), or our watchdog implementation if useful.martialebeau commentedon Aug 12, 2026
Windows corroboration + cascade link posted on the sibling issue: #41848 (comment)
Summary: on Windows (v1.18.16, ConPTY) the unbounded retry (#41848) + git exit-handler race compound — the retry loop's runaway writes feed snapshot activity, which feeds this race under index.lock contention from concurrent instances. Same un-reaped git-child pattern observed here. Full evidence (4 incidents, context trigger, ANSI flood fingerprint, 13.3 GB WAL) in the linked comment.
thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊