anomalyco / anomalyco/opencode
server: orphaned serve --service busy-loops at 100% CPU in drain retry loop (high energy)
@rekram1-node is already working on this.
Since Aug 7, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Summary
Orphaned serve --service processes can get stuck in a tight JS busy loop and pin a CPU core at ~100% for hours. Two such servers spun for ~40 minutes each on a macOS machine, showing up as "opencode2.exe — very high energy" in Activity Monitor. SIGTERM was not handled; only SIGKILL stopped them. The spins correlate with Failed to drain retry storms (HTTP 400 / 503 from the provider).
Environment
- opencode version: 0.0.0-next-16974 (
opencode2 --version) - Binary: npm package
@opencode-ai/cli, installed to/opt/homebrew/lib/node_modules/@opencode-ai/cli/bin/opencode2.exe - OS: Darwin 25.5.0 arm64 (macOS 26.5.2, Apple Silicon)
- Terminal: ghostty (TERM=xterm-256color, COLORTERM=truecolor)
- Shell: /bin/zsh
- Install/channel: next
- Active plugins:
~/.config/opencode/plugins/notify.ts
Reproduction
Observed once (intermittent). The state that produced it:
- Start an opencode TUI session that attaches to a shared
opencode2.exe serve --service. - Let the client hit repeated provider errors (see logs) and eventually disconnect (
event stream disconnected ... error=Transport), leaving theserve --serviceorphaned (reparented to launchd). - The orphaned server keeps retrying to drain the failed session and busy-loops at ~100% CPU.
- With a second session doing the same, two orphans peg two cores simultaneously.
A 1.6s sample of the spinning server (PID 1471, 99.9% CPU, 33:55 CPU in 39 min elapsed) shows the main thread hot in JIT'ed JS only (<unknown binary> frames) and all 52 threads spinning (Bun Pool, HTTP Client, JavaScriptCore libpas scavenger, Heap Helper) — a tight loop with no sleeps or I/O, not network or disk work.
Expected Behavior
serve --serviceshould exit when its last client disconnects instead of lingering orphaned.- Session drain/generation retries should back off (or at least yield to the event loop) so a failing provider does not peg a core.
Actual Behavior
- Two orphaned servers each busy-looping at ~100% CPU for ~40 minutes until killed. SIGTERM was ignored (busy loop starves signal handling); only SIGKILL worked. Combined ~200% of one core pairing for ~40 minutes — high energy consumption.
- The healthy service instance (current) runs at ~1-2% CPU, so this is not normal load.
Additional Context
The spin start times match the drain-failure storm in ~/.local/share/opencode/log/opencode.log (37 Failed to drain Session errors today). Representative errors:
Failed to drain Session ... HTTP 400: ... Invalid assistant message: content or tool_calls must be set
at <anonymous> (../ai/src/route/executor.ts:232:25)
at SessionRunner.callModel (../core/src/session/runner/llm.ts:187:32)
at SessionRunner.drain (../../node_modules/.bun/effect@.../dist/internal/effect.js:461:98)
Failed to drain Session ... Provider request failed with HTTP 503: Upstream request failed: Endpoint is unavailable.
Failed to drain Session ... openai-chat.stream: Streaming response failed: [503] The request queue is full.
at ../ai/src/protocols/openai-chat.ts:648:25
Correlation:
- First orphan (PID 1471) launched 13:21Z and was hot from launch — immediately after its session failed with HTTP 400
Invalid assistant message: content or tool_calls must be setand a client disconnect (Transport). - Second orphan (PID 2135) was idle for ~27 min, then burned 5:36 of CPU in its last ~6 min — precisely when the log shows the HTTP 503
request queue is full/Endpoint is unavailablestorm (13:37-13:56Z).
This is the next build installed the same day (0.0.0-next-16974); the retry loop in the drain path appears to have no backoff or yield, so a permanently-failing session pins a core indefinitely.
Workaround
ps -o pid,%cpu,etime,command | grep 'opencode2.exe serve --service' # find orphans (no listening socket, no live client)
kill -9 <pid>
SIGTERM does not work; use kill -9. Note the package names the binary opencode2.exe even on macOS — the .exe name is not a sign of malware.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.