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
All waves report streamsOk: 0, streamsFailed: N, and the job finishes in ~1 ms. Live monitoring (LlmProbe) keeps working, which makes it look like a network problem — it is not.
server/collectors/LlmStreaming.js creates an Agent from the npmundici package (8.9.0) and passes it as the dispatcher to the Node built-infetch (which is undici 6.28.0). Node 22's built-in fetch does not accept an 8.x Dispatcher instance and rejects it with UND_ERR_INVALID_ARG before any socket is opened.
Minimal reproduction (inside the container, with undici@8.9.0 installed):
Note that even new Agent() with default options is rejected — the failure is the instance/version boundary, not the headersTimeout: 0 options.
Where it was introduced
e03b9d6 — "feat: hide worker nodes and add remote decode/prefill benches" (2026-09-07) is the first commit that adds both the undici dependency and the dispatcher:
server/collectors/LlmStreaming.js:8 — import { Agent } from "undici";
Before that commit the streaming paths used the built-in fetch only, so benchmarks worked. Deployments still on a pre-e03b9d6 build are unaffected, which is why this can regress silently on upgrade.
The intent of the agent is legitimate: headersTimeout: 0 / bodyTimeout: 0 disables undici's 300 s idle cut so long prefill benchmarks (30–45 min caller-side timeout) are not aborted. The problem is only which package's fetch receives it.
Affected code paths
DecodeBench.js → runStreamingRequest
PrefillBench.js → runStreamingRequest
ShowcaseManager.js → runStreamingRequest
LlmProbe is unaffected because it calls fetch without a dispatcher — this is why the dashboard still shows live metrics while every benchmark fails.
Suggested fixes
Use the matching fetch — import fetch from the same undici package that owns the Agent, so both sides come from one module instance:
Verified working on the same Node 22 container (HTTP 200).
Pin undici to the 6.x line (e.g. ^6.28.0) so the dispatcher matches Node 22's built-in undici. CVE-2026-12151 is fixed in >= 6.27.0, so 6.28.0 does not reintroduce it — but this does move the dependency away from the 8.x line chosen in fix: upgrade undici to patched version (CVE-2026-12151) #81.
Move to a Node image whose built-in undici matches the npm dependency.
Option 1 keeps the current dependency version and the long-timeout behaviour, so it looks like the smallest correct change.
Additional: the error message hides the cause
describeStreamFetchError() (server/collectors/LlmStreaming.js) returns err.message for unrecognised errors, which for undici is just "fetch failed". The actionable detail lives in err.cause.code (UND_ERR_INVALID_ARG). Including the cause code in the returned string would have made this self-diagnosing from the UI:
Summary
On Node 22, every SSE-streaming path that goes through
runStreamingRequest()fails immediately withfetch failed:POST /api/sparks/:id/llm/bench)POST /api/sparks/:id/llm/prefill-bench)All waves report
streamsOk: 0,streamsFailed: N, and the job finishes in ~1 ms. Live monitoring (LlmProbe) keeps working, which makes it look like a network problem — it is not.Environment
39c7f2b(Merge PR #85), v1.8.6node:22-bookworm-slim(DockerfileARG NODE_IMAGE)package.jsondependency"undici": "^8.9.0"(resolves to 8.9.0)network_mode: hostSteps to reproduce
http://192.168.50.168:8888.Root cause
server/collectors/LlmStreaming.jscreates anAgentfrom the npmundicipackage (8.9.0) and passes it as thedispatcherto the Node built-infetch(which is undici 6.28.0). Node 22's built-in fetch does not accept an 8.xDispatcherinstance and rejects it withUND_ERR_INVALID_ARGbefore any socket is opened.Minimal reproduction (inside the container, with
undici@8.9.0installed):Cross-checks (same container, same target URL):
fetch, nodispatcherundici@8.9.0Agent → built-infetchUND_ERR_INVALID_ARGundici@6.28.0Agent → built-infetchundici@8.9.0Agent →undici@8.9.0ownfetchNote that even
new Agent()with default options is rejected — the failure is the instance/version boundary, not theheadersTimeout: 0options.Where it was introduced
e03b9d6— "feat: hide worker nodes and add remote decode/prefill benches" (2026-09-07) is the first commit that adds both theundicidependency and the dispatcher:server/collectors/LlmStreaming.js:8—import { Agent } from "undici";server/collectors/LlmStreaming.js:23-26—LLM_STREAM_AGENT = new Agent({ headersTimeout: 0, bodyTimeout: 0 })server/collectors/LlmStreaming.js:564—dispatcher: LLM_STREAM_AGENTpackage.json—"undici": "^8.9.0"Before that commit the streaming paths used the built-in
fetchonly, so benchmarks worked. Deployments still on a pre-e03b9d6build are unaffected, which is why this can regress silently on upgrade.The intent of the agent is legitimate:
headersTimeout: 0/bodyTimeout: 0disables undici's 300 s idle cut so long prefill benchmarks (30–45 min caller-side timeout) are not aborted. The problem is only which package'sfetchreceives it.Affected code paths
DecodeBench.js→runStreamingRequestPrefillBench.js→runStreamingRequestShowcaseManager.js→runStreamingRequestLlmProbeis unaffected because it callsfetchwithout a dispatcher — this is why the dashboard still shows live metrics while every benchmark fails.Suggested fixes
Use the matching
fetch— importfetchfrom the sameundicipackage that owns theAgent, so both sides come from one module instance:Verified working on the same Node 22 container (HTTP 200).
Pin
undicito the 6.x line (e.g.^6.28.0) so the dispatcher matches Node 22's built-in undici. CVE-2026-12151 is fixed in>= 6.27.0, so 6.28.0 does not reintroduce it — but this does move the dependency away from the 8.x line chosen in fix: upgrade undici to patched version (CVE-2026-12151) #81.Move to a Node image whose built-in undici matches the npm dependency.
Option 1 keeps the current dependency version and the long-timeout behaviour, so it looks like the smallest correct change.
Additional: the error message hides the cause
describeStreamFetchError()(server/collectors/LlmStreaming.js) returnserr.messagefor unrecognised errors, which for undici is just"fetch failed". The actionable detail lives inerr.cause.code(UND_ERR_INVALID_ARG). Including the cause code in the returned string would have made this self-diagnosing from the UI:Happy to open a PR for either the fix or just the error-message improvement if that helps.