René's URL Explorer Experiment


Title: http2: session.destroy() drops final GOAWAY when a write is in flight · Issue #66832 · nodejs/node · GitHub

Open Graph Title: http2: session.destroy() drops final GOAWAY when a write is in flight · Issue #66832 · nodejs/node

X Title: http2: session.destroy() drops final GOAWAY when a write is in flight · Issue #66832 · nodejs/node

Description: Version v27.0.0-pre (f88a1d25e9dc48a7f838280b236c4d74f76231a3) For comparison, the same reproducer sends GOAWAY with the installed v22.23.2 binary. Platform Linux SAGAN-10 6.18.40.1-microsoft-standard-WSL2 #1 SMP PREEMPT_DYNAMIC Fri Jul ...

Open Graph Description: Version v27.0.0-pre (f88a1d25e9dc48a7f838280b236c4d74f76231a3) For comparison, the same reproducer sends GOAWAY with the installed v22.23.2 binary. Platform Linux SAGAN-10 6.18.40.1-microsoft-stand...

X Description: Version v27.0.0-pre (f88a1d25e9dc48a7f838280b236c4d74f76231a3) For comparison, the same reproducer sends GOAWAY with the installed v22.23.2 binary. Platform Linux SAGAN-10 6.18.40.1-microsoft-stand...

Opengraph URL: https://github.com/nodejs/node/issues/66832

X: @github

direct link

Domain: github.com


Hey, it has json ld scripts:
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"http2: session.destroy() drops final GOAWAY when a write is in flight","articleBody":"### Version\n\n`v27.0.0-pre` (`f88a1d25e9dc48a7f838280b236c4d74f76231a3`)\n\nFor comparison, the same reproducer sends GOAWAY with the installed\n`v22.23.2` binary.\n\n### Platform\n\n```text\nLinux SAGAN-10 6.18.40.1-microsoft-standard-WSL2 #1 SMP PREEMPT_DYNAMIC Fri Jul 31 22:12:15 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux\n```\n\n### Subsystem\n\nhttp2\n\n### What steps will reproduce the bug?\n\nRun this standalone h2c reproducer. It queues a large response, pauses the raw\nclient so the server has a socket write in progress, then sends another HEADERS\nframe whose handler calls `session.destroy()`.\n\n```js\n'use strict';\n\nconst http2 = require('node:http2');\nconst net = require('node:net');\n\nconst PREFACE = Buffer.from('PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n');\nconst NAMES = {\n  0: 'DATA',\n  1: 'HEADERS',\n  4: 'SETTINGS',\n  7: 'GOAWAY',\n  8: 'WINDOW_UPDATE',\n};\n\nfunction frame(type, flags, streamId, payload = Buffer.alloc(0)) {\n  const header = Buffer.alloc(9);\n  header.writeUIntBE(payload.length, 0, 3);\n  header[3] = type;\n  header[4] = flags;\n  header.writeUInt32BE(streamId & 0x7fffffff, 5);\n  return Buffer.concat([header, payload]);\n}\n\nconst body = Buffer.alloc(256 * 1024 * 1024, 'a');\nconst server = http2.createServer();\nlet firstWriteDone = false;\n\nserver.on('session', (session) => session.on('error', () => {}));\nserver.on('stream', (stream) => {\n  stream.on('error', () => {});\n  if (stream.id === 1) {\n    stream.respond({ 'content-type': 'text/plain' });\n    stream.end(body, () => {\n      firstWriteDone = true;\n      console.log('first response write completed');\n    });\n    console.log('first response queued');\n    return;\n  }\n\n  console.log(`destroying; firstWriteDone=${firstWriteDone}`);\n  stream.session.destroy();\n});\n\nserver.listen(0, '127.0.0.1', () => {\n  const socket = net.connect({\n    host: '127.0.0.1',\n    port: server.address().port,\n    noDelay: true,\n  });\n  let input = Buffer.alloc(0);\n  let sawGoaway = false;\n  let totalBytes = 0;\n\n  socket.on('connect', () => {\n    socket.write(Buffer.concat([\n      PREFACE,\n      // SETTINGS_INITIAL_WINDOW_SIZE = 2^31 - 1\n      frame(4, 0, 0, Buffer.from([0x00, 0x04, 0x7f, 0xff, 0xff, 0xff])),\n      // Increase the connection window by almost 2 GiB.\n      frame(8, 0, 0, Buffer.from([0x7f, 0xff, 0x00, 0x00])),\n      frame(1, 0x05, 1, Buffer.from([0x82, 0x86, 0x84, 0x41, 0x01, 0x78])),\n    ]));\n\n    // Keep the client paused so the server's first write remains in progress.\n    setTimeout(() => {\n      socket.write(frame(\n        1,\n        0x05,\n        3,\n        Buffer.from([0x82, 0x86, 0x84, 0x41, 0x01, 0x78]),\n      ));\n    }, 1500);\n\n    setTimeout(() => {\n      socket.on('readable', readAll);\n      readAll();\n    }, 2500);\n  });\n\n  function readAll() {\n    let chunk;\n    while ((chunk = socket.read()) !== null) {\n      totalBytes += chunk.length;\n      input = Buffer.concat([input, chunk]);\n      while (input.length >= 9) {\n        const length = input.readUIntBE(0, 3);\n        if (input.length \u003c 9 + length) break;\n        const type = input[3];\n        if (type !== 0) console.log(`received ${NAMES[type] || type}`);\n        if (type === 7) sawGoaway = true;\n        input = input.subarray(9 + length);\n      }\n    }\n  }\n\n  socket.on('error', (error) => console.log(`socket error: ${error.code}`));\n  socket.on('end', () => {\n    clearTimeout(deadline);\n    console.log(\n      `RESULT sawGoaway=${sawGoaway} totalMB=${(totalBytes / 1048576).toFixed(1)}`,\n    );\n    server.close();\n  });\n\n  const deadline = setTimeout(() => {\n    console.log(`TIMEOUT sawGoaway=${sawGoaway}`);\n    socket.destroy();\n    server.close();\n  }, 8000);\n});\n```\n\n```console\n$ node repro.js\nfirst response queued\ndestroying; firstWriteDone=false\nfirst response write completed\nreceived SETTINGS\nreceived WINDOW_UPDATE\nreceived SETTINGS\nreceived HEADERS\nRESULT sawGoaway=false totalMB=256.1\n```\n\n### How often does it reproduce? Is there a required condition?\n\nIt reproduced in every run on the current `main` checkout (including a fresh\n6/6 run set and two later confirmation runs).\n\nThe required condition is that `session.destroy()` is called while an earlier\nsocket write is still in progress. In this reproducer, `destroy()` is called\nfrom a stream handler while Node is processing incoming HTTP/2 data and the\nlarge response write is stalled by the paused client.\n\n### What is the expected behavior? Why is that the expected behavior?\n\nThe peer should receive the final GOAWAY before the socket closes. The\n`http2session.destroy([error][, code])` documentation defines `code` as the\nHTTP/2 error code sent in the final GOAWAY frame.\n\nThe same reproducer on Node.js v22.23.2 produces:\n\n```text\nfirst response queued\nfirst response write completed\ndestroying; firstWriteDone=true\nreceived SETTINGS\nreceived SETTINGS\nreceived HEADERS\nreceived GOAWAY\nRESULT sawGoaway=true totalMB=256.1\n```\n\n### What do you see instead?\n\nThe current `main` checkout closes the TCP connection with a bare FIN after\nthe body. No GOAWAY frame is delivered.\n\nThis makes an intentional session shutdown indistinguishable from an abrupt\ntransport failure to peers and connection pools that use GOAWAY to decide\nwhich requests may need to be retried.\n\n### Additional information\n\nThe apparent ordering in `src/node_http2.cc` is:\n\n1. `FinishClose()` calls `nghttp2_session_terminate_session()` and\n   `SendPendingData()`, but an earlier socket write is still active, so the\n   GOAWAY remains pending in nghttp2.\n2. `FinishClose()` then marks the session destroyed.\n3. When the active write completes, `OnStreamAfterWrite()` sees\n   `is_destroyed()` and returns after the done callback, before\n   `MaybeScheduleWrite()` can serialize and send the pending GOAWAY.\n4. Calling `SendPendingData()` after destruction is not sufficient because it\n   also returns immediately when `is_destroyed()` is set.\n\nThis suggests the final destroyed/done transition needs to wait until the\nqueued terminal GOAWAY write finishes, or needs an explicit terminal-write\nstate that permits that one final flush.\n\nThe issue is adjacent to the receive-callback close handling fixed by #65116,\nbut it is a different symptom: that change prevents an assertion failure,\nwhile this report concerns loss of the final protocol frame when another write\nis already active.\n","author":{"url":"https://github.com/emerzon","@type":"Person","name":"emerzon"},"datePublished":"2026-10-18T14:58:20Z","url":"https://github.com/nodejs/node/issues/66832"}

react-profiling0
react-import-mapreact,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf
route-pattern/_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format)
route-controllervoltron_issues_fragments
route-actionissue_layout
fetch-noncev2:ba3db28f-eefe-2de5-5d41-eb33fe14518a
current-catalog-service-hash81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114
request-idC61E:334404:6DB27D:923022:6AED2109
html-safe-nonce88a3ec85cb87293a9de85f7c42d89d6fcbc81d0642c35aa733196a976fcfea94
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDNjFFOjMzNDQwNDo2REIyN0Q6OTIzMDIyOjZBRUQyMTA5IiwidmlzaXRvcl9pZCI6IjkxNjMxNTgxOTkxODE1ODI2MDEiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ==
visitor-hmac52a1a0b2431a005f05d8301e80704dd70356c7ac5060bcd37fbce953872254c4
hovercard-subject-tagissue:5910836068
github-keyboard-shortcutsrepository,issues,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///voltron/issues_fragments/issue_layout
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/nodejs/node/66832/issue_layout
twitter:imagehttps://opengraph.githubassets.com/023bc97a4f4e7b75b9e779f0f36596671c094a49c074cfccdd199c68b4c51c4b/nodejs/node/issues/66832
twitter:cardsummary_large_image
og:imagehttps://opengraph.githubassets.com/023bc97a4f4e7b75b9e779f0f36596671c094a49c074cfccdd199c68b4c51c4b/nodejs/node/issues/66832
og:image:altVersion v27.0.0-pre (f88a1d25e9dc48a7f838280b236c4d74f76231a3) For comparison, the same reproducer sends GOAWAY with the installed v22.23.2 binary. Platform Linux SAGAN-10 6.18.40.1-microsoft-stand...
og:image:width1200
og:image:height600
og:site_nameGitHub
og:typeobject
og:author:usernameemerzon
hostnamegithub.com
expected-hostnamegithub.com
Nonee3e0f7f63e50c6fe879198a93a0a023a648ecc0a5e9bc3b5e033ac57172237e6
turbo-cache-controlno-preview
go-importgithub.com/nodejs/node git https://github.com/nodejs/node.git
octolytics-dimension-user_id9950313
octolytics-dimension-user_loginnodejs
octolytics-dimension-repository_id27193779
octolytics-dimension-repository_nwonodejs/node
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id27193779
octolytics-dimension-repository_network_root_nwonodejs/node
turbo-body-classeslogged-out env-production page-responsive
disable-turbofalse
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release707a24b346ac8a82878b9c084efaa38a997660cb
ui-targetfull
theme-color#1e2327
color-schemelight dark
react-app-nameissues

Links:

Skip to contenthttps://github.com/nodejs/node/issues/66832#start-of-content
https://github.com/
Sign inhttps://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fnodejs%2Fnode%2Fissues%2F66832
GitHubRepository hosting, code review, and collaborationhttps://github.com/platform
CopilotAI coding agent for tasks, projects, and automated workhttps://github.com/features/copilot
ActionsCI/CD and automationhttps://github.com/features/actions
Code QualityQuality analysis, fixes, and enforcementhttps://github.com/features/code-quality
CodespacesCloud development environmentshttps://github.com/features/codespaces
Code SecurityVulnerability detection and remediationhttps://github.com/security/advanced-security/code-security
Secret ProtectionSecret scanning and leak preventionhttps://github.com/security/advanced-security/secret-protection
All Featureshttps://github.com/features
Documentationhttps://docs.github.com/en
Traininghttps://learn.github.com/skills
Certificationshttps://learn.github.com/credentials
Build with Copilothttps://github.com/features/ai
Customize Copilothttps://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/customize-copilot-overview
Integrationshttps://github.com/marketplace
Trending reposhttps://github.com/trending
GitHub Sponsorshttps://github.com/open-source/sponsors
Developer articleshttps://github.com/resources/articles
ChangelogYour source for the latest features, improvements, and fixes across GitHub products, all in one place.https://github.blog/changelog/
Enterpriseshttps://github.com/enterprise
Teamhttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
See all industrieshttps://github.com/solutions/industry
Why GitHubhttps://github.com/why-github
Customer Storieshttps://github.com/customer-stories
Business insightshttps://github.com/solutions/executive-insights
Trust Centerhttps://github.com/trust-center
GitHub Advanced SecurityEnterprise-grade code securityhttps://github.com/security/advanced-security
Copilot for businessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/enterprise/premium-support
Documentationhttps://docs.github.com/en
Resource centerhttps://github.com/resources
Bloghttps://github.blog/
Events & webinarshttps://github.com/resources/events
Community Forumhttps://github.com/orgs/community/discussions
Customer Storieshttps://github.com/customer-stories
Integrationshttps://github.com/marketplace
Customer supporthttps://help.github.com/
Traininghttps://learn.github.com/skills
GitHub Statushttps://www.githubstatus.com/
Abouthttps://github.com/about
Partnershttps://github.com/partners
Changeloghttps://github.blog/changelog/
GitHub UniverseThe event for developers, by developers — talks, workshops, and what’s next for GitHub.https://githubuniverse.com/
Pricinghttps://github.com/pricing
Sign inhttps://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fnodejs%2Fnode%2Fissues%2F66832
Sign uphttps://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fvoltron%2Fissues_fragments%2Fissue_layout&source=header-repo&source_repo=nodejs%2Fnode
Reloadhttps://github.com/nodejs/node/issues/66832
Reloadhttps://github.com/nodejs/node/issues/66832
Reloadhttps://github.com/nodejs/node/issues/66832
nodejshttps://github.com/nodejs
nodehttps://github.com/nodejs/node
Codehttps://github.com/nodejs/node
Issues656 (656)https://github.com/nodejs/node/issues
Pull requests666 (666)https://github.com/nodejs/node/pulls
Actionshttps://github.com/nodejs/node/actions
Projectshttps://github.com/nodejs/node/projects
Security and qualityhttps://github.com/nodejs/node/security
Insightshttps://github.com/nodejs/node/pulse
https://github.com/nodejs/node
New issuehttps://github.com/login?return_to=https://github.com/nodejs/node/issues/66832
New issuehttps://github.com/login?return_to=https://github.com/nodejs/node/issues/66832
#66834https://github.com/nodejs/node/pull/66834
http2: session.destroy() drops final GOAWAY when a write is in flighthttps://github.com/nodejs/node/issues/66832#top
#66834https://github.com/nodejs/node/pull/66834
confirmed-bugIssues and PRs for confirmed bugs.https://github.com/nodejs/node/issues?q=state%3Aopen%20label%3A%22confirmed-bug%22
http2Issues and PRs related to the http2 subsystem.https://github.com/nodejs/node/issues?q=state%3Aopen%20label%3A%22http2%22
https://github.com/emerzon
emerzonhttps://github.com/emerzon
on Oct 18, 2026https://github.com/nodejs/node/issues/66832#issue-5910836068
#65116https://github.com/nodejs/node/pull/65116
emerzonhttps://github.com/emerzon
on Oct 18, 2026https://github.com/nodejs/node/issues/66832#event-33579202646
http2: flush final GOAWAY after pending writeshttps://github.com/emerzon/node/commit/db1b93a9a001099dc1090605ef3a415cffd96c8d
db1b93ahttps://github.com/emerzon/node/commit/db1b93a9a001099dc1090605ef3a415cffd96c8d
https://github.com/inoway46
inoway46https://github.com/inoway46
on Nov 2, 2026https://github.com/nodejs/node/issues/66832#issuecomment-6581956742
RFC 9113 §6.8https://www.rfc-editor.org/rfc/rfc9113.html#section-6.8
Node.js documentationhttps://nodejs.org/api/http2.html#http2sessiondestroyerror-code
inoway46https://github.com/inoway46
confirmed-bugIssues and PRs for confirmed bugs.https://github.com/nodejs/node/issues?q=state%3Aopen%20label%3A%22confirmed-bug%22
http2Issues and PRs related to the http2 subsystem.https://github.com/nodejs/node/issues?q=state%3Aopen%20label%3A%22http2%22
on Nov 2, 2026https://github.com/nodejs/node/issues/66832#event-34929832878
Sign up for freehttps://github.com/signup?return_to=https://github.com/nodejs/node/issues/66832
Sign in to commenthttps://github.com/login?return_to=https://github.com/nodejs/node/issues/66832
confirmed-bugIssues and PRs for confirmed bugs.https://github.com/nodejs/node/issues?q=state%3Aopen%20label%3A%22confirmed-bug%22
http2Issues and PRs related to the http2 subsystem.https://github.com/nodejs/node/issues?q=state%3Aopen%20label%3A%22http2%22
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.