{"record":{"id":"44ceaebbf621d7ff","repo":"denoland/deno","slug":"err-http2-goaway-session","errorCode":"ERR_HTTP2_GOAWAY_SESSION","errorMessage":"New streams cannot be created after receiving a GOAWAY","messagePattern":"New streams cannot be created after receiving a GOAWAY","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/http2.ts","lineNumber":4692,"sourceCode":"// has been connected.\nclass ClientHttp2Session extends Http2Session {\n  constructor(options, socket) {\n    initCallbacks();\n    super(NGHTTP2_SESSION_CLIENT, options, socket);\n    this[kPendingRequestCalls] = null;\n  }\n\n  // Submits a new HTTP2 request to the connected peer. Returns the\n  // associated Http2Stream instance.\n  request(headersParam, options) {\n    debugSessionObj(this, \"initiating request\");\n\n    if (this.destroyed) {\n      throw new ERR_HTTP2_INVALID_SESSION();\n    }\n\n    if (this.closed) {\n      throw new ERR_HTTP2_GOAWAY_SESSION();\n    }\n\n    this[kUpdateTimer]();\n\n    let span;\n    if (otelState.TRACING_ENABLED) {\n      // Determine the method name for the span; mirrors the default applied\n      // by prepareRequestHeaders{Object,Array} below.\n      let spanMethod;\n      if (ArrayIsArray(headersParam)) {\n        for (let i = 0; i < headersParam.length; i += 2) {\n          if (\n            StringPrototypeToLowerCase(headersParam[i]) === HTTP2_HEADER_METHOD\n          ) {\n            spanMethod = headersParam[i + 1];\n            break;\n          }\n        }","sourceCodeStart":4674,"sourceCodeEnd":4710,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/http2.ts#L4674-L4710","documentation":"Thrown by ClientHttp2Session.request() when the session is closed — this.closed is true — which happens after a GOAWAY frame is received (server graceful shutdown) or close() has been initiated. Per HTTP/2, a GOAWAY tells the client the server will not accept new streams, so request() refuses with ERR_HTTP2_GOAWAY_SESSION even though existing streams may still drain. The destroyed check runs first, so a not-yet-destroyed but closed session produces exactly this error.","triggerScenarios":"Issuing request() after the peer sent GOAWAY (graceful shutdown, server restart, LB draining); racing the 'goaway' event — you picked the session before GOAWAY arrived but called request() after; calling request() after your own session.close().","commonSituations":"Long-lived clients against servers that rotate connections periodically (e.g. after N requests or T seconds); load-balancer draining windows; connection pools with no GOAWAY awareness, so requests keep failing until the pool refreshes.","solutions":["Catch ERR_HTTP2_GOAWAY_SESSION (and ERR_HTTP2_INVALID_SESSION), create a new session via http2.connect(), and retry the request — the request was never sent, so a retry is safe","Listen for the session 'goaway' event and proactively evict the session from your pool","For servers that GOAWAY after N requests, rotate sessions yourself before hitting the limit"],"exampleFix":"// before\nconst stream = session.request(headers); // throws after server sent GOAWAY\n\n// after\nasync function requestRetry(headers) {\n  try {\n    return session.request(headers);\n  } catch (e) {\n    if (e.code !== 'ERR_HTTP2_GOAWAY_SESSION' && e.code !== 'ERR_HTTP2_INVALID_SESSION') throw e;\n    session = http2.connect(origin);\n    return session.request(headers);\n  }\n}","handlingStrategy":"retry","validationCode":"if (session.closed || session.destroyed) {\n  session = http2.connect(origin);\n}\nconst stream = session.request(headers);","typeGuard":"function canOpenNewStream(s) {\n  return !s.destroyed && !s.closed;\n}","tryCatchPattern":"try {\n  stream = session.request(headers);\n} catch (e) {\n  if (e.code !== 'ERR_HTTP2_GOAWAY_SESSION' && e.code !== 'ERR_HTTP2_INVALID_SESSION') throw e;\n  session = http2.connect(origin); // GOAWAY means the request never left\n  stream = session.request(headers);\n}","preventionTips":["Listen for the 'goaway' event and drain/evict pooled sessions immediately","The request that hits this error was never sent — retrying it on a new session is safe","Existing streams survive GOAWAY; only NEW streams are refused"],"tags":["http2","node-compat","goaway","client","graceful-shutdown"],"backgroundTag":"http2-goaway-received","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}