Fix synthesis fetch failures from Node's default 5-minute HTTP timeout

Nothing published for hours, every cluster failing with "fetch failed".
Ollama's own log showed the real story: requests being cancelled at
exactly 5m0s with a 500, not a model or server error. Node's global
fetch (undici) defaults to a 5-minute headers/body timeout, and CPU-only
prompt processing on the reference hardware (i5-6600K, no GPU, ~17
tok/s) legitimately takes longer than that once prompts carry full
article bodies instead of short blurbs (the previous fix in this same
line of work) — every generate() call past a few thousand tokens got
killed client-side before Ollama could finish.

OllamaProvider.generate() now passes a dedicated undici Agent with
headersTimeout/bodyTimeout disabled as the fetch dispatcher, so the
request runs as long as it actually needs to. Verified the failure
mode and the fix directly: a short-timeout dispatcher against a
deliberately slow server reproduces the exact same "fetch failed" /
UND_ERR_HEADERS_TIMEOUT error seen in production, and a zero-timeout
dispatcher completes the same slow request without issue.

undici was already a transitive dependency (via jsdom); added directly
since ollama-provider.ts now imports from it.
This commit is contained in:
Claude
2026-07-27 13:10:50 +00:00
parent e063d90c97
commit adb2783f1b
3 changed files with 23 additions and 4 deletions
+2 -1
View File
@@ -15,7 +15,8 @@
"fastify": "^5.10.0",
"jsdom": "^29.1.1",
"rss-parser": "^3.13.0",
"telegram": "^2.26.22"
"telegram": "^2.26.22",
"undici": "^7.28.0"
},
"devDependencies": {
"@types/jsdom": "^28.0.3",
+2 -1
View File
@@ -18,7 +18,8 @@
"fastify": "^5.10.0",
"jsdom": "^29.1.1",
"rss-parser": "^3.13.0",
"telegram": "^2.26.22"
"telegram": "^2.26.22",
"undici": "^7.28.0"
},
"devDependencies": {
"@types/jsdom": "^28.0.3",
+19 -2
View File
@@ -1,5 +1,19 @@
import { Agent } from 'undici';
import type { InferenceProvider } from './provider.js';
/**
* Node's global fetch (undici) defaults to a 5-minute headers/body timeout — fine for
* ordinary HTTP calls, but a real problem for /api/generate on CPU-only inference: a
* near-full context window can legitimately take longer than that just for prompt
* processing on the reference hardware (i5-6600K, no GPU, ~17 tokens/sec). Once
* synthesis prompts started carrying full article bodies instead of short blurbs, every
* generate() call past a few thousand tokens got killed at exactly 5m0s — visible in
* Ollama's own log as the request being cancelled, not a genuine model/server error —
* so no cluster could ever finish synthesizing. No timeout at all here; Ollama's own
* process is the natural backstop, not a clock tuned for hardware this doesn't run on.
*/
const noTimeoutDispatcher = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
/**
* Default context window / max-generation length requested from Ollama when a caller
* doesn't specify its own. Ollama otherwise falls back to whatever the model's
@@ -51,8 +65,11 @@ export class OllamaProvider implements InferenceProvider {
num_ctx: opts.numCtx ?? DEFAULT_NUM_CTX,
num_predict: opts.numPredict ?? DEFAULT_NUM_PREDICT
}
})
});
}),
// Not in the ambient RequestInit type this project resolves to, but Node's global
// fetch (built on undici) honors it at runtime — see noTimeoutDispatcher above.
dispatcher: noTimeoutDispatcher
} as RequestInit);
if (!res.ok) throw new Error(`Ollama generate failed: ${res.status} ${await res.text()}`);
const data = (await res.json()) as { response: string };
return data.response;