You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Intermittent multi-second first-request stall on Bun serve under CPU load (node:http dispatch starvation) #46437
On Bun, opencode serve intermittently hangs its first request for tens of seconds under CPU load, then recovers (startup-only; once warm it's fine).
I traced it end to end: the request-handler fiber is never forked — the TCP connection is accepted and the request bytes are read into userspace (rx_queue=0), but Bun's node:http never emits the JS request event while the loop is parked in epoll_pwait2, so Effect never sees the request. Bisected to Bun: 1.3.11 does not stall; 1.3.12/1.3.13/1.3.14 do (rebuilding opencode on 1.3.11 drops the rate from ~100% to ~5% under the same load) — so it's a Bun regression under load, not app logic. Effect's default scheduler flushes fiber dispatch via globalThis.setImmediate, which hits the same starvation.
Proposed fix in #46436 (route the scheduler flush through a MessageChannel macrotask).
OpenCode version
1.18.18 (Bun 1.3.14)
Steps to reproduce
Run opencode serve on a CPU-constrained container (e.g. a 2 vCPU cgroup) under load.
Fire the first request as soon as it's listening (a warm-up GET /session/:id is enough).
Under heavy contention the first request hangs ~100% of the time for 30–60s before self-clearing; under lighter load it's intermittent (a few percent of cold boots).
Operating System
Debian 12 (bookworm) container, Linux 6.17 x86_64, cgroup-limited to 2 vCPU / 4 GiB, Docker + overlayfs.
The trigger is a request that arrives in the window between the socket starting to accept connections and node:http dispatch being wired. Firing the first request the instant the port accepts reproduces it 100% of the time; a ~0.5s pause after listen avoids it every time. CPU load only widens that window (which is why it looks intermittent in a real deployment), it is not required.
Environment verified: Debian 12 (bookworm), cgroup-limited 2 vCPU (cpu.max = 200000 100000), overlayfs (sysbox container), Linux 6.8 x86_64, official opencode1.18.18 release binary (opencode-linux-x64.tar.gz, embeds Bun 1.3.14). Only bash + curl are needed.
# 1. exact official artifactcd /tmp && curl -fsSL -o oc.tgz https://github.com/anomalyco/opencode/releases/download/v1.18.18/opencode-linux-x64.tar.gz
mkdir -p oc && tar -xzf oc.tgz -C oc && B=$(find oc -name opencode -type f | head -1)&& chmod +x "$B""$B" --version # 1.18.18# 2. one trial: start serve, fire the first request the INSTANT the port accepts, time it
PW=x
trial () { # $1=delay after listen (s) $2=curl timeout (s)
rm -rf ~/.local/share/opencode ~/.local/state/opencode/locks 2>/dev/null
OPENCODE_SERVER_PASSWORD=$PW"$B" serve --port 4096 >/tmp/oc.log 2>&1</dev/null &
sp=$!foriin$(seq 1 1200);do (echo >/dev/tcp/127.0.0.1/4096) 2>/dev/null &&break; sleep 0.05;done
sleep "$1"
r=$(curl -s -m "$2" -o /dev/null -w '%{http_code} %{time_total}' -u "opencode:$PW" http://127.0.0.1:4096/session/ses_warmup)
r2=$(curl -s -m 20 -o /dev/null -w '%{http_code} %{time_total}' -u "opencode:$PW" http://127.0.0.1:4096/session/ses_warmup)echo"delay=${1}s first: http=${r%%*} t=${r#* }s | next request: http=${r2%%*} t=${r2#* }s"kill -9 $sp2>/dev/null;wait2>/dev/null; sleep 1
}
# 3. reproduce: immediate fire -> the first request never gets a responsefornin 1 2 3;do trial 0 60;done# 4. control: same thing with a 0.5s pause after listen -> fast every timefornin 1 2 3;do trial 0.5 30;done
Results (9 immediate-fire trials, all stalled; 3 delayed, all fast)
condition
first request
trials
immediate fire, no load, cold page cache
000 @ 60s (hung)
1/1
immediate fire, no load, warm page cache
000 @ 60s (hung)
2/2
immediate fire, 4× yes hogs on 2 vCPU
000 @ 60s (hung)
4/4
immediate fire, 2× yes hogs
000 @ 60s (hung)
2/2
immediate fire, 150s timeout
000 @ 150s — does not self-clear
1/1
0.5s pause after listen
404 in 0.05–0.13s
2/2
2s pause after listen
404 in 0.11s
1/1
Two corrections to the current description, from the data:
The stalled request never clears. With a 150s client timeout it is still hung at 150s; it is not a 30–60s delay. What looks like "self-clears after ~60s" in a deployment is the client (curl -m 60) giving up. The connection is orphaned: bytes are accepted but the request event is never emitted for it.
The server is otherwise healthy. The very next request on the same process is served in ~0.1–0.2s. So it's a lost dispatch for the first connection, not a frozen event loop.
Page-cache state (cold vs warm binary) and CPU load made no difference to the hit rate — the only variable that matters is whether the first request lands inside the startup window.
confirming the deterministic repro on 1.18.33 and 1.18.34 (Linux x86_64, no CPU limit, 4 to 8 free cores). Measured from the first TCP accept: GET /global/health at +0, +50, +100, +150, +200 ms never gets a response, at +250 ms and later it returns 200 in about 5 ms. The listening on line prints at about +250 ms, so the window is the gap between bind and the handler being wired. 3 of 3 runs on each version.
Would be great to get #46436 or an equivalent reviewed. The stalled connection never clears, so any client that connects right after bind and retries with backoff can land in the window and hang for its full timeout.
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.
Activity
aman-kumar-gw commentedon Sep 2, 2026
Deterministic reproduction (no CPU load needed)
The trigger is a request that arrives in the window between the socket starting to accept connections and
node:httpdispatch being wired. Firing the first request the instant the port accepts reproduces it 100% of the time; a ~0.5s pause after listen avoids it every time. CPU load only widens that window (which is why it looks intermittent in a real deployment), it is not required.Environment verified: Debian 12 (bookworm), cgroup-limited 2 vCPU (
cpu.max = 200000 100000), overlayfs (sysbox container), Linux 6.8 x86_64, officialopencode1.18.18 release binary (opencode-linux-x64.tar.gz, embeds Bun 1.3.14). Onlybash+curlare needed.Results (9 immediate-fire trials, all stalled; 3 delayed, all fast)
000@ 60s (hung)000@ 60s (hung)yeshogs on 2 vCPU000@ 60s (hung)yeshogs000@ 60s (hung)000@ 150s — does not self-clear404in 0.05–0.13s404in 0.11sTwo corrections to the current description, from the data:
curl -m 60) giving up. The connection is orphaned: bytes are accepted but therequestevent is never emitted for it.Page-cache state (cold vs warm binary) and CPU load made no difference to the hit rate — the only variable that matters is whether the first request lands inside the startup window.
feat(project-snapshot): S3 boot acquires the project 3.5× faster than…
axsuul commentedon Oct 1, 2026
confirming the deterministic repro on 1.18.33 and 1.18.34 (Linux x86_64, no CPU limit, 4 to 8 free cores). Measured from the first TCP accept:
GET /global/healthat +0, +50, +100, +150, +200 ms never gets a response, at +250 ms and later it returns 200 in about 5 ms. Thelistening online prints at about +250 ms, so the window is the gap between bind and the handler being wired. 3 of 3 runs on each version.Would be great to get #46436 or an equivalent reviewed. The stalled connection never clears, so any client that connects right after bind and retries with backoff can land in the window and hang for its full timeout.
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 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊