Skip to content

fix(h2): prevent uncaughtException from onHttp2SocketError accessing undefined kClient#5546

Open
mcollina wants to merge 3 commits into
mainfrom
fix/h2-uncaught-socket-error
Open

fix(h2): prevent uncaughtException from onHttp2SocketError accessing undefined kClient#5546
mcollina wants to merge 3 commits into
mainfrom
fix/h2-uncaught-socket-error

Conversation

@mcollina

Copy link
Copy Markdown
Member

Root Cause

In client-h2.js, onHttp2SocketError accesses this[kClient] where this is the socket. However, kClient is only stored on the session (session[kClient] = client), never on the socket. So this[kClient] is undefined, and calling this[kClient][kOnError](err) throws a TypeError. This thrown error inside the 'error' event handler becomes an uncaughtException.

Changes

  1. onHttp2SocketError — Route through this[kHTTP2Session]?.[kClient] instead of this[kClient]
  2. util.destroy — Wrap stream.destroy(err) in try-catch (http2-managed sockets throw on direct destroy())
  3. onHttp2SessionEnd — Wrap util.destroy(this[kSocket], err) in try-catch (same reason)

Testing

All 1421 existing tests pass (1418 pass, 3 skipped).

Fixes #5525

…undefined kClient

The onHttp2SocketError handler accesses this[kClient] where this is the
socket, but kClient is only stored on the session (session[kClient]),
never on the socket. This causes a TypeError when the error handler is
invoked, which becomes an uncaughtException.

Fix by routing through this[kHTTP2Session]?.[kClient] instead.

Also wraps stream.destroy(err) in util.destroy and onHttp2SessionEnd
in try-catch, since http2-managed sockets throw ERR_HTTP2_NO_SOCKET_MANIPULATION
on direct destroy() calls.

Fixes #5525
@codecov-commenter

codecov-commenter commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 57.14286% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.45%. Comparing base (c7c7e7f) to head (f0b477e).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
lib/core/util.js 50.00% 3 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #5546   +/-   ##
=======================================
  Coverage   93.45%   93.45%           
=======================================
  Files         110      110           
  Lines       37376    37416   +40     
=======================================
+ Hits        34928    34967   +39     
- Misses       2448     2449    +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…ket close

Verifies that aborting an in-flight HTTP/2 request followed by the
socket closing does not trigger an uncaughtException. This exercises
the onHttp2SocketError fix where kClient must be accessed through
the session rather than directly on the socket.
@mcollina

Copy link
Copy Markdown
Member Author

The underlying .destroy() throw is fixed in Node core: nodejs/node#64427

Http2Session is a plain EventEmitter and never emits 'end', so this
listener could never fire.
Comment thread test/http2-dispatcher.js
Comment on lines +1105 to +1107
after(() => {
server.close()
})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use t.after

Suggested change
after(() => {
server.close()
})
t.after(() => {
server.close()
})

Ditto at other places.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

uncaughtException in unidici.request()

3 participants