Replies: 1 comment
|
Your mechanism holds up against HEAD ( The harness never passes Two refinements to the suggestion:
Patchability today: none via config. Not verified: why the poisoned session persisted 4 minutes despite the 8.10.x reset paths (you couldn't reproduce standalone either), and your before/after tables — those are your measurements. If you capture a poisoned run with |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
TL;DR
After a single TLS-layer failure, every subsequent model request failed in 1–2 ms with
ERR_HTTP2_INVALID_SESSION("The session has been destroyed") and never recovered — only restartingdshfixed it. Forcing the process to HTTP/1.1 (undici global dispatcher withallowH2: false)restores recoverability: the underlying connection errors still happen, but each one is now an
ordinary retryable request failure that recovers within the same turn — no restart, and zero
ERR_HTTP2_INVALID_SESSIONsince.Measured before/after (same machine, same route, same provider):
ERR_HTTP2_INVALID_SESSIONUND_ERR_SOCKET: other side closed)Symptom and evidence
Captured at the fetch layer (
error.causechains), DeepSeek Harness0.1.5-rc.2, Nodev26.8.2:While poisoned, the local tunnel logged no DNS query and no TCP connection for those requests —
i.e. the failures were entirely local, consistent with re-use of a destroyed HTTP/2 session rather
than a reconnect. Retries could not help, and raising
retryPolicy.maxRetriesonly changed how longeach turn waited before dying.
Mechanism
undici enables HTTP/2 by default —
allowH2 = allowH2 != null ? allowH2 : true(
undici/lib/core/connect.js), ALPN['http/1.1','h2']. Against an h2-capable edge an HTTP/2 sessionis negotiated; once that session is destroyed (here by a TLS record failure on the wire), the client
keeps re-using it instead of reconnecting, so every later request fails locally.
Note the installed undici is 8.10.2, which already contains nodejs/undici#5310 ("reset invalid
HTTP/2 sessions") and #5453 ("requeue request on GOAWAY'd session") —
lib/dispatcher/client-h2.jshas the
ERR_HTTP2_INVALID_SESSION→resetHttp2Session()+requeueUnsentRequest()path — yet thesession stayed poisoned for 4 minutes here. A standalone reproduction was not achieved: three
self-contained attempts (graceful h2 session destroy, socket destroy between requests, socket destroy
mid-stream) all reconnected correctly on the next request in undici 8.10.2. The production failure
needs the TLS-layer failure to occur on a live h2 connection.
Mitigation (verified)
Install a global dispatcher that never negotiates h2:
Verification that it takes effect (the same process also uses Node's built-in
fetch, whosedispatcher is shared through the undici global symbol):
HTTP 200 ALPN=["h2"]new Agent({ allowH2: false })HTTP 200 ALPN=["http/1.1"]HTTP/1.1 has no session-level state: a broken connection is an ordinary socket error and the next
request opens a fresh connection. We run this as a small local plugin that installs the dispatcher
after
dsh-http-proxydoes, with a guard that skips overriding when a proxy dispatcher is alreadyinstalled (so
HTTPS_PROXYsetups are not broken), and logs the before/after dispatcher on everyboot for verification.
What this does not claim
saw 18
UND_ERR_SOCKET: other side closedevents in ~1.1 h (17 of them at ~1 s elapsed: connect +TLS, then closed by the peer; one at 17.9 s). Who closes them — the gateway, an intermediary, or the
local tunnel — is not determined; no speculation here.
The claim is narrow and falsifiable: if a poisoned h2 session returns,
ERR_HTTP2_INVALID_SESSIONwill reappear here. So far it has not.
are expected on this route; what must never happen again is a session that can only be recovered by
restarting the process.
Suggestion for the harness
In
@deepseek-ai/dsh-http-proxy's direct (no-proxy) path, install the agent asnew undici.Agent({ allowH2: false }), or reset the dispatcher/connection when aTRANSPORTfailurecarries
ERR_HTTP2_INVALID_SESSION. That converts a "process is dead until restart" failure into asingle retryable request error — which is what we measured after applying it locally.
Related
allowH2.All reactions