denoland/deno · error · Error
ERR_HTTP2_INVALID_SESSION
ERR_HTTP2_INVALID_SESSION
Error message
The session has been destroyed
What it means
Thrown by Http2Session.setNextStreamID() when the session's destroyed flag is already true. Like Node's http2 core, this polyfill refuses any method that mutates protocol state once the session is torn down, because the underlying nghttp2 handle no longer exists. setNextStreamID sets the ID of the next stream this session will create, which only has meaning on a live session.
Source
Thrown at ext/node/polyfills/http2.ts:4152
}
// Resets the timeout counter
[kUpdateTimer]() {
if (this.destroyed) {
return;
}
if (this[kTimeout]) {
this[kTimeout].refresh();
syncSessionTimeoutInspectLinks(this[kTimeout]);
}
}
// Sets the id of the next stream to be created by this Http2Session.
// The value must be a number in the range 0 <= n <= kMaxStreams. The
// value also needs to be larger than the current next stream ID.
setNextStreamID(id) {
if (this.destroyed) {
throw new ERR_HTTP2_INVALID_SESSION();
}
validateNumber(id, "id");
if (id <= 0 || id > kMaxStreams) {
throw new ERR_OUT_OF_RANGE("id", `> 0 and <= ${kMaxStreams}`, id);
}
this[kHandle].setNextStreamID(id);
}
// Sets the local window size (local endpoints's window size)
// Returns 0 if success or throw an exception if NGHTTP2_ERR_NOMEM
// if the window allocation fails
setLocalWindowSize(windowSize) {
if (this.destroyed) {
throw new ERR_HTTP2_INVALID_SESSION();
}
validateInt32(windowSize, "windowSize", 0);View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Check session.destroyed (and session.closed) before calling setNextStreamID, and reconnect first if either is true
- Recreate the session with http2.connect(origin) and call setNextStreamID on the fresh session before the first request()
- Move the call into the session's 'connect' event handler so it always runs on a healthy session
Example fix
// before
session.setNextStreamID(nextId); // throws ERR_HTTP2_INVALID_SESSION if destroyed
// after
if (session.destroyed || session.closed) {
session = http2.connect(origin);
}
session.setNextStreamID(nextId); Defensive patterns
Strategy: validation
Validate before calling
if (session.destroyed || session.closed) {
session = http2.connect(origin);
}
session.setNextStreamID(nextId); Type guard
function isSessionUsable(s) {
return !s.destroyed && !s.closed;
} Try / catch
try {
session.setNextStreamID(id);
} catch (e) {
if (e.code === 'ERR_HTTP2_INVALID_SESSION') { session = http2.connect(origin); session.setNextStreamID(id); }
else throw e;
} Prevention
- Treat a session as unusable the moment its 'close' event fires
- Keep session creation and its initial configuration (setNextStreamID, settings) in one place so they share a lifetime
- Never cache an Http2Session across reconnect cycles without a liveness check
When it happens
Trigger: Calling http2session.setNextStreamID(id) after session.destroy(), after a socket 'error'/'close' event auto-destroyed the session, or from a callback/timer that fires after teardown finished.
Common situations: Reusing a cached or pooled ClientHttp2Session after the server closed the connection; test code that destroys the session in an afterEach hook while a pending callback still calls setNextStreamID; long-lived processes that keep one session across reconnects without checking liveness.
Related errors
- ERR_HTTP2_SOCKET_UNBOUND
- ERR_HTTP2_HEADERS_SENT
- ERR_INVALID_ARG_TYPE
- ERR_HTTP2_PAYLOAD_FORBIDDEN
- ERR_HTTP2_HEADERS_AFTER_RESPOND
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/498bba5749202638.
Report an issue: GitHub.