HTTP transport: after a tools/call hits the server-side -32000 Request timeout, the handler still tries to answer → ERR_HTTP_HEADERS_SENT in the log
Version: ruflo@3.38.20 (ruflo mcp start -t http), Node 22, Linux container. Observed on a private bridge image (cognitum-one/ruclip) with --tools set to an exact-name allowlist.
What happened
- First
memory_store on a fresh process (embedding model not yet loaded in-process) was answered by the server after exactly 30.008 s with:
{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"Request timeout"}}
- The handler then completed the store anyway (the value was retrievable afterwards) and attempted to write its real response to the same, already-finished
ServerResponse. The process logged:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (node:_http_outgoing:700:11)
at Layer.handleError (/app/node_modules/router/lib/layer.js:116:17)
- A second
memory_store ~1 min later took 24.95 s and succeeded; every store after that was 13–15 ms. So the timeout path is racing the lazy embedder load, and the late write is what trips the router.
Why it matters
- Every store after the first is fine, so this only shows on cold start — but it is exactly what a Cloud Run startup probe sees, and the stray write is a silent second response path (the client already got
-32000; nothing tells it the store actually persisted).
- The 30 s timeout is not configurable from the CLI as far as we can find, and it is shorter than a cold ONNX session init on a slow host.
Suggested fix
- Cancel/detach the handler when the timeout fires (or mark the response as sent and skip the late
setHeader), and
- expose the request timeout as a flag/env (
--request-timeout-ms) so hosted bridges can set it above their measured cold-start.
Workaround we shipped: a readiness wrapper that keeps the real process warm and does not open the public port until an in-process memory_store has succeeded (cognitum-one/ruclip, ADR-0002 "Cold start").
Related: #3155 (bridge state loss), #3157 (per-session rate limit).
HTTP transport: after a
tools/callhits the server-side-32000 Request timeout, the handler still tries to answer →ERR_HTTP_HEADERS_SENTin the logVersion: ruflo@3.38.20 (
ruflo mcp start -t http), Node 22, Linux container. Observed on a private bridge image (cognitum-one/ruclip) with--toolsset to an exact-name allowlist.What happened
memory_storeon a fresh process (embedding model not yet loaded in-process) was answered by the server after exactly 30.008 s with:{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"Request timeout"}}ServerResponse. The process logged:memory_store~1 min later took 24.95 s and succeeded; every store after that was 13–15 ms. So the timeout path is racing the lazy embedder load, and the late write is what trips the router.Why it matters
-32000; nothing tells it the store actually persisted).Suggested fix
setHeader), and--request-timeout-ms) so hosted bridges can set it above their measured cold-start.Workaround we shipped: a readiness wrapper that keeps the real process warm and does not open the public port until an in-process
memory_storehas succeeded (cognitum-one/ruclip, ADR-0002 "Cold start").Related: #3155 (bridge state loss), #3157 (per-session rate limit).