Skip to content

node:http2: close an answered, unread server stream once its response has left - #46058

Open
robobun wants to merge 4 commits into
mainfrom
robobun/c4dcac70/http2-close-unread-server-stream
Open

robobun wants to merge 4 commits into
mainfrom
robobun/c4dcac70/http2-close-unread-server-stream

Conversation

@robobun

@robobun robobun commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Merge after #45801, #37985 and #43754 (Downsides says why).

Problem

Fix

  • maybeCloseUnreadServerStream now runs from markWritableDone (the response's END_STREAM left the native queue) and from finish. The later call queues the close one setImmediate later.
  • It skips an ended request and waits for StreamState.WritableClosed, so a reset cannot drop a queued END_STREAM.
  • Verified: test/js/node/http2/h2-conformance.test.ts (52 new tests, main fails 42). Also node-http2.test.js, 279 upstream test-http2-*, grpc-js.
  • Self-reviewed: 14 concerns raised, 12 addressed. Open: one test row (probed instead), the merge gates.

Background

  • In bun finish can fire before the handler resumes behind an await, so the close tests again.
  • Rejected: node's rule as written (it cuts responses) and a reset-order rule in Rust (JS waits instead).

Downsides

Notes

Origin. No user reported this and there is no issue. A maintainer asked for it in team chat with four repro scripts.

Merge gates.

What replaces the gate of #32488. bb67830 added readableLength > 0 because the END_STREAM of a request without a body could reach JS after finish, so nearly every such response got a reset. Now:

Node versions. All of this ran in the build container.

  • The four scripts of the request, unchanged, as node <script>: v26.3.0 exits 0, 0, 0, 0. v26.12.0 (the latest release) exits 0, 0, 0, 0. v27.0.0-nightly20261018f88a1d25e9 exits 1, 2, 2, 1: no RST_STREAM, no close, no server.close() callback, 0 of 4 fresh requests answered, the honest user of the gateway answered 0 of 5 times.
  • The cause on node main is autoDrainReadable (merge commit 5f9618049b27 of Fix HTTP/2 RST_STREAM behaviour, add auto-drain, deprecate 'aborted', fix related compat API issues nodejs/node#63249). It is a finish listener of every server stream and calls resume() on an unread request. The close branch of kMaybeDestroy then sees readableFlowing !== null. The tags v26.12.0, v26.3.0 and v24.22.0 do not have the function. bun reports process.version v26.3.0.
  • One client shape against four servers. The client is a POST that has not ended when the 401 arrives, once with a data listener at once and once with for await started 300 ms after response. Servers that close (node v26.12.0, this branch) give the same rows: node v26.3.0 and v26.12.0 clients get the body with both readers and the late iterator throws ERR_STREAM_PREMATURE_CLOSE (http2: async iterator rejects complete buffered response after NO_ERROR reset with request side open nodejs/node#66693). The node nightly client and bun 1.4.3-canary get the body with the early reader and an empty body with the late one. Servers that do not close (node nightly, bun main): every client gets the body with both readers, and the stream stays open.
  • The script of http2: server sends RST_STREAM(NO_ERROR) without END_STREAM when the connection window is exhausted, truncating complete responses nodejs/node#66525 (the connection window is used up when the compat API submits its END_STREAM frame): v26.3.0 and v26.12.0 send RST_STREAM with no END_STREAM before it. bun main and this branch send the END_STREAM. With the small request left open, this branch sends DATA, DATA(END_STREAM), RST_STREAM(0).
  • Server stream events for HEADERS without END_STREAM, respond() + end(): finish, end, close with rstCode 0 and aborted false, on node v26.3.0 and on this branch.

Where bun now differs from node v26 on purpose. For a request that never ends, node v26.3.0 and bun main send no reset in these 8 shapes, and this branch sends RST_STREAM(NO_ERROR): compat end() without a body, compat statusCode = 204, compat writeHead(205), compat writeHead(304), compat end() for a HEAD request, respond({ endStream: true, waitForTrailers: true }), a HEAD request with respond() + end(), and a read() that returned null before the answer. Node holds these for trailers that never come. With one unread body byte buffered, main already resets all eight and node none. The table in the test file has node's frames beside bun's for 25 shapes. 17 are equal.

What still holds a stream.

So "server.close() calls back" holds for answered, unread requests. It does not hold for the cases above.

Frames after the reset. Measured with a raw client, answer first, then the rest of the request 3 s later:

Measured. main is 5900354 (release 1.4.3-canary), PR is this branch. N=200 per class. The counts of the PR are the same on a debug build and on a release build of this branch.

  • setImmediate per request, in the order GET / POST with a 100-byte body and END_STREAM in the same write, unread / POST read then answered / one body byte, never ended / nothing more, never ended: main 0.00 / 1.00 / 0.00 / 3.00 / 0.00, PR 0.00 / 1.00 / 0.00 / 3.00 / 3.00. POST with an empty DATA+END_STREAM in the same write, answered at once: 0.00 -> 1.00.
  • Per complete GET, compat and respond()+end(): once('finish') registrations 1 -> 0. Calls of the check 0 -> 1 (it returns after one property read). setImmediate 0 -> 0.
  • Upload of 8 DATA chunks to a handler that reads: calls of the check 8 -> 0, getter reads 24 -> 0.
  • instructions:u per GET and per DATA chunk: not measured. perf, valgrind and strace are not installed in the build container.
  • Responses cut by the automatic close (RST_STREAM with no END_STREAM before it), socket held back, 8 MiB compat response: main cuts 2 of 4 request shapes (1000 unread body bytes, request open or complete), PR 0 of 4.
  • Real trailers behind 200 kB of DATA that a 1 KiB stream window holds back, 8 combinations (end(chunk) or file, trailers at once or one immediate later, with and without 1000 upload bytes): node v26.3.0, main and this branch deliver the whole response in 8 of 8. The frames of this branch equal node's in 8 of 8. This is the probe that stands in for a test row.
  • Builtin node/http2 module, release codegen: 175,338 -> 175,552 bytes (+214). No host function and no codegen row is added.
  • Release binary, the same tree with and without the change to http2.ts: .text 106,771,236 -> 106,771,464 bytes (+228). The file is 106,944,224 bytes in both builds.
  • 200 answered requests that never end: close events 0 -> 200, live stream objects after GC 200 -> 0.
  • bun's node:http2 client against a bun server, a 100 KiB response to a request that never ends, three reader shapes: 3 of 3 get the whole body from main, 2 of 3 from this branch. The reader attached 50 ms after response gets 0 bytes (node:http2: keep the data that arrived before a peer's RST_STREAM(NO_ERROR) #43754). The client of node v26.3.0 gets 3 of 3 from this branch.
  • Suites on the debug build: h2-conformance.test.ts 0 fail of 136. Upstream test-http2-*: 0 fail of 279. node-http2.test.js: 0 fail, 405 pass, 6 skip (with --timeout 30000, see Tests).
  • The four unchanged scripts of the request exit 0, 0, 0, 0 on a release build of this branch (local build of cef5d11) and 1, 1, 1, 1 on bun 1.4.3-canary. With the debug build of this branch as the server they exit 0, 0, 0, 0 as well, there with clients on 1.4.3-canary, the gateway on node, and the waits of the slots client times 10.

Tests.

  • One guard removed per run, on a private copy of the debug binary: the 52 tests catch the removal of the ended-request skip, headersSent, WritableClosed, the file rule, the second test in the immediate, the call in markWritableDone, the finish hook, and the swaps of setImmediate for a microtask or nextTick and of close() for destroy(). They do not catch the removal of four guards that main already has (destroyed || closed twice, writableFinished, the unread test before the immediate) or of the skip of the finish hook for an ended request. Those change the count of immediates or registrations only.
  • In the build container the page cache holds about 400 MB, so each spawn reads the 1.2 GB debug binary again. node-http2.test.js gives 405 pass, 0 fail, 6 skip with --timeout 30000. With the default 5 s, 6 to 19 of its subprocess tests time out there, and 9 on the unfixed build. The new describe is the last one in h2-conformance.test.ts. The reset-flood cases of that file take 3.4 to 7.5 s on the debug build in the build container, against the 5 s default. Interleaved runs show the same range with the new describe ahead of them or behind them, and main's file with main's build is in that range as well.
  • The stream-release cases of the file (liveCount()) fail in 7 of 8 runs on a debug build in the build container, with and without this PR (4 to 8 of 16 streams alive on both builds). test: deflake the h2 stream-release cases on debug builds #42357 is about them. This PR does not change them.
  • The four socket held the response back rows send requests until the session reports a queued response. They do not depend on the buffer sizes of the host.
  • Two reset-flood tests held answered uploads open across a GOAWAY. Their handler now reads the request, which is what keeps such a stream open.
  • The state "END_STREAM held by flow control after finish" cannot be built in bun: the empty END_STREAM frame leaves at once, and finish waits for DATA that flow control holds. Socket backpressure and late trailers reach the WritableClosed test.

Interim workaround on a released bun. It polls, and it cannot stop main's own cut.

server.on("stream", s => s.once("finish", function look() {
  if (s.closed || s.destroyed || s.readableDidRead || s.readableFlowing !== null) return;
  if (s.state.localClose !== 1) return void setTimeout(look, 5);
  setImmediate(() => { if (!s.closed && !s.destroyed && !s.readableDidRead && s.readableFlowing === null) s.close(); });
}));

Also seen, not changed here.

  • A server stream.end() without respond() and without a chunk sends DATA+END_STREAM with no HEADERS frame. Node sends nothing. Main does the same.
  • In bun finish fires before a microtask that the handler queued (deferWriteCallbackForSocket completes a flushed write on process.nextTick). In node the microtask runs first. Main does the same.
  • Not run by me: macOS, Windows. CI runs them.

… has left

Node through v26 closes a server stream with RST_STREAM(NO_ERROR) when its
response has finished and nobody tried to read its request
(Http2Stream[kMaybeDestroy]). Bun did this only when unread request bytes
were buffered. A stream whose client sent HEADERS without END_STREAM and
nothing more stayed open: it kept its maxConcurrentStreams slot, and
server.close() did not call back.

The check now runs where the END_STREAM of the response leaves the native
queue (markWritableDone) and at 'finish', whichever comes last, and it
tests again one immediate later. This replaces the readableLength gate and
the call per inbound DATA frame that #32488 added. A request that has
ended is skipped, and a reset can no longer drop a queued END_STREAM.

Node 27 drains the unread request instead of the reset
(nodejs/node#63249).
@robobun

robobun commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 4:42 PM PT - Oct 19th, 2026

✅ @robobun, your commit 8b4fc7720c00871a79864d4a07ffb38d56d94ffe passed in Build #129004! 🎉


🧪   To try this PR locally:

bunx bun-pr 46058

That installs a local version of the PR into your bun-46058 executable, so you can run:

bun-46058 --bun

@robobun

robobun commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status: ready for review. One decision is open for the requester: close (node through v26, this PR) or drain (node main, nodejs/node#63249). Merge after #45801, #37985 and #43754.

Reproduction: the four repro scripts of the request, unchanged, exit 1, 1, 1, 1 on bun 1.4.3-canary and 0, 0, 0, 0 on a release build of this branch. Node v26.3.0 and v26.12.0 exit 0, 0, 0, 0.

test/js/node/http2/h2-conformance.test.ts has 52 new cases. 42 of them fail without the src/ change, and all pass with it.

Tests: all 181 CI jobs pass on 8b4fc77, Windows and macOS included. The annotated tests are not http2 tests and passed on retry. On the debug build of my container one existing case of this file fails in each run, with main's file and main's build as well: a reset-flood case goes over the 5 s default, or a stream-release case fails (#42357).

PR: #46058

…rooted streams

The new describe ran ahead of the reset-flood cases and made them slower:
4.9 to 7.3 s against the 5 s default on a debug build, and 2.5 to 3.6 s
with the describe at the end of the file.

liveCount() takes the change of #42357. It stops when no more than
GC_STRAGGLERS streams are left, and no longer when the count holds for 10
passes. Without it the stream-release cases fail in 7 of 8 runs on a debug
build, with and without the change of this branch.
@robobun
robobun marked this pull request as ready for review October 19, 2026 16:38
@robobun
robobun requested a review from cirospaciari October 19, 2026 16:38
@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: oven-sh/bun/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 6504efd5-0ef6-4c1f-96e9-d6752879f1e3
📥 Commits

Reviewing files that changed from the base of the PR and between 6303fd9 and 0901e1a.

📒 Files selected for processing: 1
  • test/js/node/http2/h2-conformance.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.

Walkthrough

The HTTP/2 server now applies updated conditions when closing streams with unread requests. The conformance suite adds coverage for response completion, stream closure, transport variations, flow control, and request-reader timing.

Changes

Unread server request streams

Layer / File(s) Summary
Unread request closure logic
src/js/node/http2.ts
Writable completion now checks eligible server streams for unread-request closure. Closure requires the readable to remain active, the response to finish with headers sent, the writable side to be closed, and file-response trailers to be complete. Deferred closure rechecks stream state. The server finish hook is skipped if the readable has ended, and the request-data retry was removed.
Response completion and closure tests
test/js/node/http2/h2-conformance.test.ts
The raw test client accepts a port or read/write stream. The suite adds helpers and cases for unread-request closure, event ordering, concurrent-stream capacity, response-ending variants, and trailers.
Transport, flow control, and reader timing tests
test/js/node/http2/h2-conformance.test.ts
Additional cases cover backpressure, connection-window contention, TCP, TLS and duplex-pair transports, late request data, client aborts, handlers without responses, and readers attached at different times. GC lifecycle limits and the reset-flood helper are also updated.

Priority: ⬆️ High

Merge Risk: ⚪ Minimal · up to 0901e

This change closes answered HTTP/2 server streams whose requests were never read, and adds conformance tests for that behavior. No concrete merge-blocking issue was established. The PR reports that macOS and Windows were not tested.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the primary change: closing an answered, unread HTTP/2 server stream after its response is sent.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description clearly explains the problem, implementation, intended behavior, known tradeoffs, and verification results. It includes extensive test coverage and identifies limitations, untested pla…
Full details: Description check

Explanation

The description clearly explains the problem, implementation, intended behavior, known tradeoffs, and verification results. It includes extensive test coverage and identifies limitations, untested platforms, and follow-up issues. Although it does not use the exact template headings, it provides the required information.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Beyond the inline findings, I also checked whether the two triggers (markWritableDone and the 'finish' hook) can send two RST_STREAM frames for one stream: close() sets StreamState.Closed synchronously via markStreamClosed, so the second queued closeUnreadServerStream returns on stream.closed. I also checked that dropping the streamData trigger does not reopen the original deadlock: END_STREAM-less uploads are now caught by the WritableClosed path in markWritableDone, and a request whose END_STREAM arrives later ends the readable before the immediate runs, so the re-check in closeUnreadServerStream skips it.

Extended reasoning...

The change rewrites the server-side auto-close for answered, unread node:http2 streams in src/js/node/http2.ts (two triggers, removed trailer and readableLength guards, removed streamData trigger) and adds ~700 lines of raw-frame conformance tests. It touches no auth or crypto surface, but it changes wire behavior (RST_STREAM emission) toward proxies and clients and deliberately diverges from node v26 in eight response shapes. The confirmed inline findings on late-DATA/trailer handling, the widened empty-body case for late readers, and the host-dependent heldBack assertion, plus the author's own request for a maintainer review of the removed Windows gate, mean a human look is needed.

Findings marked 🟡 are optional suggestions and need no follow-up push.

Comment thread src/js/node/http2.ts
function closeUnreadServerStream(stream: ServerHttp2Stream) {
if (stream.destroyed || stream.closed) return;
// Tested again: 'finish' can come before the handler attaches its reader behind an await.
if (!stream.readableDidRead && stream.readableFlowing === null) stream.close();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Operators running a Bun http2 server behind a proxy such as nginx grpc_pass can now get requests failed with "upstream rejected request with error 5" when a handler answers before the upload ends. The auto-close at src/js/node/http2.ts:3210 sends RST_STREAM(NO_ERROR) while the client still has DATA frames in flight, and the engine answers each late DATA frame with RST_STREAM(STREAM_CLOSED) instead of ignoring it (RFC 9113 §5.1). Fix: before this close runs routinely, make the engine ignore DATA and HEADERS on a stream it reset itself (keep the closed entry or a recently-reset set) so the only reset the peer sees is the NO_ERROR one, as node/nghttp2 do.

Why this was flagged

A client (grpc-js, nginx grpc_pass, any uploader) sends HEADERS without END_STREAM, the server handler responds and ends without reading the body (e.g. an auth rejection), and the client has more DATA frames in flight. closeUnreadServerStream at src/js/node/http2.ts:3210 calls stream.close(), which sends RST_STREAM(NO_ERROR) via rstNextTick (src/js/node/http2.ts:2598). Every DATA frame that then arrives hits src/runtime/api/bun/h2/connection.rs:1498 or :1504 and gets RST_STREAM(STREAM_CLOSED); the PR's own test comment at test/js/node/http2/h2-conformance.test.ts:2723 states this. On the base branch no reset is sent for a request with no buffered bytes, so the peer sees the whole response and no STREAM_CLOSED. nginx's grpc module treats a later RST_STREAM with a non-zero code as an upstream error (the author reports seeing 'upstream rejected request with error 5'), so the downstream client loses the answer. No safeguard prevents it: the engine only tracks streams in its map and has no record of streams it reset itself.

Verification: src/js/node/http2.ts:2108-2110 (new): markWritableDone now calls maybeCloseUnreadServerStream for any not-yet-Closed ServerHttp2Stream; :3210 closeUnreadServerStream calls stream.close() when nothing has read the request. Every later DATA frame for that id then hits the None arm: connection.rs:1637 → :1654 send_rst_stream for full frames, or :1498 for streamed frames.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is correct, and it is the second bullet of Downsides.

I measured it with a raw client: the answer first, then 3 late DATA frames. Node v26.3.0 sends RST_STREAM(0) and nothing more. This branch sends RST_STREAM(0), then RST_STREAM(STREAM_CLOSED) once per late DATA frame. Main gives the same reply on a stream that it reset itself (it resets when unread body bytes are buffered). So the defect is in the engine, and this PR makes it reachable for more requests.

The engine change that you describe is #45801 (late DATA) and #37985 (late HEADERS on a server). Both are open. This PR does not copy them. It has to merge after them, and the description says so.

I did not run nginx against this branch. The log line in the notes comes from a run with the rule emulated on a release binary.

Comment thread src/js/node/http2.ts
if (stream[kFileResponseFinal] !== undefined && (status & StreamState.WantTrailer) === 0) return;
if (!stream.readableDidRead && stream.readableFlowing === null) {
setImmediate(callStreamClose, stream);
setImmediate(closeUnreadServerStream, stream);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 pre-existing, widened: Bun http2 clients that attach their response reader after an await now get an empty body from a Bun server whenever their request is still open when the answer finishes. The new close at src/js/node/http2.ts:3201 sends RST_STREAM(NO_ERROR) for every such stream, not only when body bytes were buffered. On the client that reset reaches emitStreamErrorNT, whose stream.resume() at src/js/node/http2.ts:3774 dumps the buffered response before any late 'data' listener or for-await exists; node's client keeps it. Fix: on a peer NO_ERROR reset keep the buffered readable and defer destroy to 'end', as the streamEnd(7) branch at src/js/node/http2.ts:5106-5110 already does, so both early and late readers get the body.

Why this was flagged

A Bun http2 client sends POST HEADERS, the body DATA+END_STREAM goes out in a later write. The Bun server handler answers without reading (404, 401, 413). After this change markWritableDone (src/js/node/http2.ts:2112-2114) and serverStreamOnFinish (src/js/node/http2.ts:3205) schedule closeUnreadServerStream, which calls stream.close() at src/js/node/http2.ts:3210; the base only did this when readableLength > 0 at 'finish', so a request with no body byte buffered was never reset. The client engine turns the RST_STREAM(NO_ERROR) into onStreamError(0); the ClientHttp2Session handler at src/js/node/http2.ts:5077-5082 runs emitStreamErrorNT, which at src/js/node/http2.ts:3773-3776 calls stream.resume() and push(null) and then destroy(). resume() flows the buffered response DATA on nextTick with no listener attached, so a handler that does await once(req, 'response'); await something; req.on('data', ...) or starts for-await later gets an empty body. The safeguard closeUnreadServerStream re-checks the server-side reader, not the client-side one, so it does not help.

Verification: src/js/node/http2.ts:3205 no longer gates serverStreamOnFinish on readableLength > 0, so :3210 stream.close() sends RST_STREAM(NO_ERROR) for an open request with no buffered body bytes, which the base never reset. In emitStreamErrorNT, :3773-3776 runs stream.resume() with no 'data' listener attached, so the buffered response bytes are discarded. A reader attached afterward sees an empty body and no error.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Correct. I measured it with a 100 KiB response to a request that never ends:

  • bun's client gets the whole body with a data listener attached at once, and with for await started right after response.
  • bun's client gets 0 bytes with a reader attached 50 ms after response.
  • Against main all three readers get the body, because main sends no reset for a request without a buffered body byte.
  • The client of node v26.3.0 gets the body in all three cases.

The client change that you describe is #43754. It is open and has conflicts. The description now says that a late reader can lose the response until #43754 lands. I keep the client change out of this PR, so that the two do not carry the same code.

Comment thread test/js/node/http2/h2-conformance.test.ts Outdated
Comment thread test/js/node/http2/h2-conformance.test.ts Outdated
The four "socket held the response back" rows required that one 8 MiB
response is more than the socket takes. The buffers of the host decide
that. The rows now send requests until the session reports a queued
response, and they check that stream.

The liveCount() comment is shorter and names its source.

Correction to the message of the commit before this one: the place of the
new describe in the file does not change how long the reset-flood cases
take. Interleaved runs on a debug build give 3.4 to 7.5 s for them with
either placement.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code review found no new issues

No new issues were found in this update; 3 findings from earlier reviews are still open above.

Still open from earlier reviews (3):

  • 🔴 src/js/node/http2.ts:3201 — pre-existing, widened: Bun http2 clients that attach their response reader after an await now get an empty body from a…
  • 🔴 src/js/node/http2.ts:3210 — Operators running a Bun http2 server behind a proxy such as nginx grpc_pass can now get requests failed with "upstream…
  • Also unresolved: 1 minor or pre-existing.

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

The change to liveCount() belongs to #42357, which has the heap snapshot
for it. This branch does not touch the stream-release cases.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code review found no new issues

No new issues were found in this update; 2 findings from earlier reviews are still open above.

Still open from earlier reviews (2):

  • 🔴 src/js/node/http2.ts:3201 — pre-existing, widened: Bun http2 clients that attach their response reader after an await now get an empty body from a…
  • 🔴 src/js/node/http2.ts:3210 — Operators running a Bun http2 server behind a proxy such as nginx grpc_pass can now get requests failed with "upstream…

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

This branch has not been deployed

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants