anomalyco / anomalyco/opencode

server: orphaned serve --service busy-loops at 100% CPU in drain retry loop (high energy)

Open
#41,066 1 comment 0 reactions 1 assignee View on GitHub

@rekram1-node is already working on this.

Since Aug 7, 2026.

2.0
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:

  1. Start an opencode TUI session that attaches to a shared opencode2.exe serve --service.
  2. Let the client hit repeated provider errors (see logs) and eventually disconnect (event stream disconnected ... error=Transport), leaving the serve --service orphaned (reparented to launchd).
  3. The orphaned server keeps retrying to drain the failed session and busy-loops at ~100% CPU.
  4. 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 --service should 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 set and 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 unavailable storm (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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.