Skip to content

dev server: do not change route state for a request answered before it is parked - #42850

Open
robobun wants to merge 3 commits into
mainfrom
robobun/a5ef195f/dev-server-413-route-state
Open

robobun wants to merge 3 commits into
mainfrom
robobun/a5ef195f/dev-server-413-route-state

Conversation

@robobun

@robobun robobun commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • Dev server with framework routes (Bun.serve({ app: { framework } }), behind the bake feature flag). The first request for a cold route is rejected with 413 because its Content-Length is over maxRequestBodySize. The next request for that route aborts the process with panic: infallible: bundle active (release) or assertion failed: dev.current_bundle.is_some() (debug).
  • DevServer::defer_request (src/runtime/bake/DevServer.rs:2116) returns without parking the request when prepare_and_save_js_request_context returns None: the response is already written. ensure_route_is_bundled (line 1927) did not know that. It still queued the route, set it to Bundling, and started a bundle with nothing parked. finalize_bundle settles a route only through its parked requests and promise, so the route stayed Bundling after the bundle ended.

Fix

  • on_defer and defer_request return a new Deferral enum: Parked or Answered.
  • The three sites in ensure_route_is_bundled call on_defer first. On Answered they return before route_queue.put, the state write, and start_async_bundle. The route stays Unqueued, so the next request bundles it as usual.
  • The promise context (bundleNewRoute) always returns Parked. Its behavior does not change.
  • Verified: test/bake/dev/bundle.test.ts (new test, fails on main with the panic above). Also test/bake/framework-router.test.ts, test/bake/dev/plugins.test.ts, test/bake/dev/html.test.ts.

Background

  • A framework route has a server_state: Unqueued, DeferredToNextBundle, Bundling, PossibleBundlingFailures, or Loaded. ensure_route_is_bundled drives a request through these states.
  • A request for a route that is not Loaded is parked: defer_request saves it in current_bundle.requests or next_bundle.requests. finalize_bundle walks that list, marks each waiting route Loaded, and replays the requests.
  • prepare_and_save_js_request_context (src/runtime/server/server_body.rs) can answer the request itself before it is saved: 413 for a body over the limit, 400 for HTTP/3 with transfer-encoding. That is the Answered case.
Notes
  • Reach: this only affects HandlerKind::ServerHandler (framework routes under Bun.serve({ app })). HTML bundle routes use BundledHtmlPage and never take the Answered path. The report (dev server: a request rejected before it is parked (413) leaves its framework route in Bundling, and the next request panics #42838) came from a code read, not from a user.
  • With a bundle already active, the same path left the route DeferredToNextBundle in route_queue with nothing parked. The first site in the Unqueued arm covers that case too.
  • The Bundling and DeferredToNextBundle arms also call on_defer. On Answered they already return without a state change, so they need no edit.
  • append_route_entry_points_if_not_stale runs before on_defer in the last site. It only reads the graphs, so the early return leaves no partial state.
  • dev server: answer queued requests when the next bundle has nothing to bundle #42830 fixes a different entry into the same panic (a queued route whose next bundle has nothing to bundle). The two diffs touch different functions.
  • Self-reviewed: the review asked for the reach note above and for the PR to state that start_next_bundle_if_present and on_plugins_rejected can still advance a route without a waiter. Those are separate paths and stay out of this diff.
  • Repro from the issue: bun repro413.mjs prints POST -> 413, GET /about -> connection ECONNRESET, server DIED SIGABRT on main. With this diff: GET /about -> 200 <body>about</body>, server alive.

[human-review] gate passed · iteration 1 · 2 files touched

fails on main (without fix)
ASAN without fix: 1 FAILED
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/pr_gate.xml" test/bake/dev/bundle.test.ts
bun test v1.4.3 (09bb54630)

test/bake/dev/bundle.test.ts:
Dev server testing directory: /tmp/bun-dev-test-a08p2X
�[0;30mdev|�[0m Started development server: http://localhost:38155
�[0;30mdev|�[0m �[32mBundled page in 172ms�[0m�[2m:�[0m routes/index.ts �[2m+ 1 more�[0m
�[0;30mdev|�[0m [Bun] Hot update was not accepted because it or its importers do not call `import.meta.hot.accept`. To prevent full page reloads, call `import.meta.hot.accept` in one of the following files to handle the update:
�[0;30mdev|�[0m 
�[0;30mdev|�[0m Module "db.ts" is not accepted by routes/index.ts,
�[0;30mdev|�[0m �[32mReloaded in 44ms�[0m�[2m:�[0m db.ts
�[0;30mdev|�[0m [Bun] Hot update was not accepted because it or its importers do not call `import.meta.hot.accept`. To prevent full page reloads, call `import.meta.hot.accept` in one of the following files to handle the update:
�[0;30mdev|�[0m 
�[0;30mdev|�[0m Module "routes/index.ts" is a root module that does not self-accept.
�[0;30mdev|�[0m �[36m[x2]�[0m �[32mReloaded in 64ms�[0
... (truncated)

release without fix: 1 FAILED
bun test v1.4.3-canary.1 (09bb54630)

test/bake/dev/bundle.test.ts:
Dev server testing directory: /tmp/bun-dev-test-4FYJzQ
�[0;30mdev|�[0m Started development server: http://localhost:35537
�[0;30mdev|�[0m �[32mBundled page in 5ms�[0m�[2m:�[0m routes/index.ts �[2m+ 1 more�[0m
�[0;30mdev|�[0m [Bun] Hot update was not accepted because it or its importers do not call `import.meta.hot.accept`. To prevent full page reloads, call `import.meta.hot.accept` in one of the following files to handle the update:
�[0;30mdev|�[0m 
�[0;30mdev|�[0m Module "db.ts" is not accepted by routes/index.ts,
�[0;30mdev|�[0m �[32mReloaded in 7ms�[0m�[2m:�[0m db.ts
�[0;30mdev|�[0m [Bun] Hot update was not accepted because it or its importers do not call `import.meta.hot.accept`. To prevent full page reloads, call `import.meta.hot.accept` in one of the following files to handle the update:
�[0;30mdev|�[0m 
�[0;30mdev|�[0m Module "routes/index.ts" is a root module that does not self-accept.
�[0;30mdev|�[0m �[36m[x2]�[0m �[32mReloaded in 2ms�[0m�[2m:�[0m routes/index.ts
(pass)  DEV:bundle-1: import identifier doesnt get renamed [225.65ms]
�[0;30mdev|�[0m Started development server: http://localhos
... (truncated)
passes on PR (with fix)
ASAN with fix: all passed
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/pr_gate.xml" test/bake/dev/bundle.test.ts
bun test v1.4.3 (09bb54630)

test/bake/dev/bundle.test.ts:
Dev server testing directory: /tmp/bun-dev-test-zBfoZV
�[0;30mdev|�[0m Started development server: http://localhost:43279
�[0;30mdev|�[0m �[32mBundled page in 132ms�[0m�[2m:�[0m routes/index.ts �[2m+ 1 more�[0m
�[0;30mdev|�[0m [Bun] Hot update was not accepted because it or its importers do not call `import.meta.hot.accept`. To prevent full page reloads, call `import.meta.hot.accept` in one of the following files to handle the update:
�[0;30mdev|�[0m 
�[0;30mdev|�[0m Module "db.ts" is not accepted by routes/index.ts,
�[0;30mdev|�[0m �[32mReloaded in 42ms�[0m�[2m:�[0m db.ts
�[0;30mdev|�[0m [Bun] Hot update was not accepted because it or its importers do not call `import.meta.hot.accept`. To prevent full page reloads, call `import.meta.hot.accept` in one of the following files to handle the update:
�[0;30mdev|�[0m 
�[0;30mdev|�[0m Module "routes/index.ts" is a root module that does not self-accept.
�[0;30mdev|�[0m �[36m[x2]�[0m �[32mReloaded in 44ms�[0
... (truncated)

release with fix: all passed
$ bun scripts/build.ts --profile=release
[configured] bun-profile → bun (stripped) in 1091ms (unchanged)
ninja: Entering directory `/workspace/bun/build/release'
[1/7] gen generated_host_exports.rs
generated_host_exports.rs: 121 exports (host=5, lazy=10, generic=106, rust=0); 243 extern-C blocks audited
[1/7] cargo bun_runtime → libbun_runtime.a
�[1m�[92m   Compiling�[0m bun_install v0.0.0 (/workspace/bun/src/install)
�[1m�[92m   Compiling�[0m bun_jsc v0.0.0 (/workspace/bun/src/jsc)
�[1m�[92m   Compiling�[0m bun_js_parser_jsc v0.0.0 (/workspace/bun/src/js_parser_jsc)
�[1m�[92m   Compiling�[0m bun_semver_jsc v0.0.0 (/workspace/bun/src/semver_jsc)
�[1m�[92m   Compiling�[0m bun_ast_jsc v0.0.0 (/workspace/bun/src/ast_jsc)
�[1m�[92m   Compiling�[0m bun_patch_jsc v0.0.0 (/workspace/bun/src/patch_jsc)
�[1m�[92m   Compiling�[0m bun_css_jsc v0.0.0 (/workspace/bun/src/css_jsc)
�[1m�[92m   Compiling�[0m bun_sourcemap_jsc v0.0.0 (/workspace/bun/src/sourcemap_jsc)
�[1m�[92m   Compiling�[0m bun_bundler_jsc v0.0.0 (/workspace/bun/src/bundler_jsc)
�[1m�[92m   Compiling�[0m bun_http_jsc v0.0.0 (/workspace/bun/src/http_jsc)
�[1m�[92m   Compiling�[0m bun_sql_jsc v0.0.0 (/workspace
... (truncated)
diff hotspot
src/runtime/bake/DevServer.rs | 52 +++++++++++++++++++++++++++----------------
 test/bake/dev/bundle.test.ts  | 48 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+), 19 deletions(-)

gate history · 1 passed · 0 rejected · iteration 1

evidence per changed file
file                           reads  edits  tests
src/runtime/bake/DevServer.rs      3      6     11
test/bake/dev/bundle.test.ts       1      1     11

root cause · written by the author bot

The dev server moved a cold framework route into the Bundling state before it knew whether the request could actually be parked as a waiter, so a request answered early (for example with 413 for an oversized Content-Length) left the route marked as bundling with no bundle running and nothing to settle it. Because finalize_bundle only resolves a route through its waiters, the next request for that route found Bundling with no current bundle and hit the infallible assertion. The fix makes the deferral path report whether the request was Parked or Answered, and when it was answered…

… before it was parked

defer_request writes a 413 or 400 response and parks nothing when
prepare_and_save_js_request_context returns None. ensure_route_is_bundled
still queued the route, set it to Bundling, and started a bundle that
finalize_bundle could never settle. The next request for the route then
panicked with "infallible: bundle active".

on_defer now reports whether the request was parked. When it was answered,
the route stays Unqueued and no bundle starts.

Fixes #42838
@robobun

robobun commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 8:28 PM PT - Sep 15th, 2026

✅ @robobun, your commit 9ad375714ab4183ae21c53a19cefd3494067d8ea passed in Build #116252! 🎉


🧪   To try this PR locally:

bunx bun-pr 42850

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

bun-42850 --bun

@coderabbitai

coderabbitai Bot commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Essentials

Run ID: d0dfac8b-9ba2-455b-a514-da5527454c81

📥 Commits

Reviewing files that changed from the base of the PR and between 931411f and 2970c0b.

📒 Files selected for processing (1)
  • test/bake/dev/bundle.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 0 remain after this review.


Walkthrough

Changes

Deferral outcome handling

Layer / File(s) Summary
Deferral outcome propagation
src/runtime/bake/DevServer.rs
Deferral APIs now return Parked or Answered through request and promise contexts.
Bundling state and regression coverage
src/runtime/bake/DevServer.rs, test/bake/dev/bundle.test.ts
Answered requests remain unqueued. A regression test verifies that an oversized POST returns HTTP 413 and does not block later /about requests.

Priority: ⚪ Pending latest changes

Merge Risk: ⚪ Minimal · up to 2970c

The change keeps answered requests out of bundling while preserving recovery for later requests; no concrete merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly summarizes the main change: preventing route-state changes when a request is answered before parking.
Description check ✅ Passed The description explains the problem, fix, scope, background, and verification results. It does not use the exact template headings, but it provides the required information and is complete.

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 moving on_defer ahead of route_queue.put changes the bundleNewRoute (promise) path — it does not, since PromiseEnsureRouteBundledCtx::on_defer already does the same route_queue.put for the route before the outer put runs, and always returns Parked. The Answered early return in defer_request also leaves the claimed deferred_request_pool slot released without drop_in_place, matching the pre-existing None path.

Extended reasoning...

Findings were reported inline, so this body only records what else was examined. The reorder in the three Unqueued sub-paths is behavior-preserving for the promise context because that context's on_defer itself puts the route into route_queue (idempotent second put afterwards) and registers the promise; the request context's Answered path returns before any state write, and the pool slot claimed in defer_request is dropped uninitialized exactly as on the old None path. The remaining concerns (test hang wiring, test flakiness against the unfixed build, uncovered sibling sites, and the still-live waiter-less advancement paths in start_next_bundle_if_present/on_plugins_rejected) are covered by the inline comments and are the reason a human should look.

One verified lower-impact observation (a convention, logging or cleanup point) was not posted.

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

Additional findings (outside the current diff — GitHub can't attach inline comments there):

  • 🟣 src/runtime/bake/DevServer.rs — Dev server users whose deferred route has nothing left to bundle still get the panic: infallible: bundle active abort this PR's title promises to remove. start_next_bundle_if_present marks every queued route Bundling at DevServer.rs:4961, then skips start_async_bundle at DevServer.rs:4966 when entry_points is empty, so the route stays Bundling with current_bundle None and its parked request is never answered. Fix: when no bundle starts, settle the queued routes (mark them Loaded or PossibleBundlingFailures and replay next_bundle.requests) instead of leaving them Bundling; the referenced #42830 is not in this checkout. [also at: src/runtime/bake/DevServer.rs:4961 - Clients whose request was parked for a queued route are never answered when the next bundle has nothing to compile, and the route is left Bundling with no bundle.]

    Extended reasoning...

    The dismissal relied on the PR text saying #42830 covers this. git log --all --grep=42830 finds nothing in this checkout, so after merge the path is live. Trigger: bundle A is active for route /a whose page module imports /b's page module. A request for /b arrives, hits the Unqueued arm at DevServer.rs:1939, on_defer returns Parked, /b is put in route_queue and set DeferredToNextBundle. Bundle A finishes and bundles /b's files as imports. finalize_bundle_cleanup calls start_next_bundle_if_present at DevServer.rs:3799. next_bundle.requests.first is non-null, so the loop at 4958 sets /b to Bundling and append_route_entry_points_if_not_stale adds nothing because every file is already in bundled_files. entry_points.set is empty, start_async_bundle is skipped at 4966, route_queue is cleared at 4971. /b is Bundling with current_bundle None; the parked request stays in next_bundle.requests. The next request for /b enters the Bundling arm at DevServer.rs:2068, on_defer(CurrentBundle) hits .expect("infallible: bundle active") at line…

    Verification: pre-existing (acknowledged in diff: the PR description defers this path to "#42830", which is not in this checkout — git log --all --grep=42830 is empty; the note that it is "a different entry into the same panic" is accurate). Triggering condition: a route is parked as DeferredToNextBundle while another bundle is active, and that bundle happens to bundle all of the deferred route's…

  • 🟣 src/runtime/bake/DevServer.rs — After a plugin load fails, every later request for a route that was queued behind the plugins hangs forever, and debug builds trip an assertion. on_plugins_rejected at DevServer.rs:6229-6238 aborts the parked requests and clears route_queue but never resets those routes' server_state, so they stay DeferredToNextBundle. The arm at DevServer.rs:2056-2064 then parks each new request in next_bundle.requests with nothing scheduled to start a bundle, and the debug_assert at 2057 fires. Fix: any path that drops waiters without a bundle (on_plugins_rejected, the empty-entry-points skip) must also return the affected routes to Unqueued, so the next request re-enters the plugin-error or fresh-bundle path.

    Extended reasoning...

    The finder filed this as pre_existing because the diff does not touch these entries. The PR's own thesis is that a route must never be advanced without a waiter, and this PR keeps that invariant only at three sites while the sibling site that removes waiters is left inconsistent. Trigger: cold server, first request for /a. Unqueued arm, plugin_state Unknown, get_or_load_plugins returns Pending (DevServer.rs:1983), plugin_state becomes Pending, the request is parked at 2003 and /a is put in route_queue and set DeferredToNextBundle at 2007. The user's plugin setup throws. on_plugins_rejected runs: plugin_state = Err, the parked request is aborted with an error page, route_queue is cleared at 6238. /a's server_state is still DeferredToNextBundle. The user fixes nothing yet and reloads /a. ensure_route_is_bundled reads DeferredToNextBundle, debug_assert at 2057 fails in debug builds; in release, on_defer(NextBundle) parks the request in next_bundle.requests and returns. No current bundle exists, plugins are Err, nothing calls start_next_bundle_if_present, so the browser spins until the idle…

    Verification: pre-existing (the base already fails by the same route; this diff touches the Pending arm that produces the state but does not introduce or widen the defect; PR description explicitly notes on_plugins_rejected "can still advance a route without a waiter" and leaves it out of scope). Trigger: a server with async serve_plugins whose plugin load rejects after at least one framework-route…

Comment thread test/bake/dev/bundle.test.ts
Comment thread test/bake/dev/bundle.test.ts
Comment thread test/bake/dev/bundle.test.ts
…ixed code

A request for a second route waits until the stray bundle ends, so the
next request for the first route always finds the route stuck. Reject
the raw-socket promise on close so that a dropped connection fails fast.
Comment thread src/runtime/bake/DevServer.rs Outdated
Comment thread src/runtime/bake/DevServer.rs Outdated
@robobun

robobun commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator Author

Review follow-up, as of 9ad3757:

  • The test now sends a request for a second route between the 413 and the first GET. On the unfixed code that request waits for the stray bundle, so the GET always finds the route stuck. With the base src/ under the debug build the test fails with assertion failed: dev.current_bundle.is_some(). With the fix it passes.
  • The raw-socket promise rejects on close when no status line arrived.
  • The two findings outside the diff (start_next_bundle_if_present with no entry points, and on_plugins_rejected leaving routes DeferredToNextBundle) are separate paths. dev server: answer queued requests when the next bundle has nothing to bundle #42830 covers the first. The second is pre-existing and stays out of this PR.

@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 issues

No high-confidence issues detected in this change.

One verified lower-impact observation (a convention, logging or cleanup point) was not posted.

@robobun

robobun commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator Author

A second branch reached the same fix (the same three guards) before this PR existed. That branch is dropped, and no second PR will be opened. It had two tests that this PR does not have. They cover the two guards that the test here does not reach:

  • request rejected with 413 while another bundle runs does not queue the route (the current_bundle.is_some() path)
  • request rejected with 413 while plugins load does not queue the route (the PluginState::Pending path, with [serve.static] plugins in bunfig.toml)

Both tests are in one test-only commit on top of this PR's head (9ad3757): 8991d95 on robobun/5946ccf5/dev-server-413-more-tests. The diff is +101 lines in test/bake/dev/bundle.test.ts, with no change under src/. To take it:

git fetch origin robobun/5946ccf5/dev-server-413-more-tests && git merge --ff-only FETCH_HEAD

Checked on a debug build with the DevServer.rs of this PR:

  • All three 413 tests pass.
  • With the first two guards removed and the third kept, the test of this PR still passes and both new tests fail.
  • On main, both new tests fail.

Jarred-Sumner added a commit that referenced this pull request Oct 21, 2026
…6270)

### What does this PR do?

Hardening follow-up to #46021. It fixes regressions from #46021 and
#44618 that were never released, includes more of the open pull requests
that fix a crash, and fixes a few crashes that had no pull request.

#### Regressions fixed (none was released)

- `spyOn(Bun, "$")`, and the same for `"sql"`, `"postgres"` and `"SQL"`,
threw "does not support accessor properties yet".
- Async `fs.read`, `fs.write` and `zlib.brotliCompress` on a view over a
`WebAssembly.Memory` could lose data or copy far more than they needed.
- `node:vm` with `vm.constants.DONT_CONTEXTIFY`: `this` in host
accessors, prototype handling, `util.types.isProxy`, `Bun.deepEquals`,
`assert.deepEqual` and `console.log` of a context.
- `zlib` `stream.flush()` threw for arguments it used to accept, and
streams had a new enumerable property.
- `Bun.Transpiler` `exports.replace` with the name `"default"`.
- `process.env.X = 3000` could store the number once the function was
JIT-compiled.
- `node:sqlite` and `bun:sqlite` could lose rows at exit.
- Dev server: stylesheets shared between pages after a build error.
- Shell `cp` of a file onto itself exited 1.
- `error.stack`: the frame of `require()` reads `at anonymous (unknown)`
again.
- `import * as process from "process"` followed by `process.on(...)`
threw at load. The EventEmitter methods that the module exports now act
on `process`.
- `delete res.statusCode; res.end()` in a `node:http` handler threw. It
sends 200, as in Node. (from #46128)
- `node:tls`, `node:https` and `node:http2`: connection teardown and
write callbacks behave as in 1.4.3 and Node again.

#### Crash fixes

Bundler, parser and CSS: #42286, #46092, #46097, #46177, #46239, #36334,
#43024, #40769.
`bun test`, `--hot` and `--watch`: #30776, #38206, #38974.
Shell: #43183, #37743, #46168, #46207, #34900.
`node:http`, `node:http2`, `Bun.serve`, streams: #43449, #45380, #44457,
#42197, #42485, #38082, #38124, #46074, #45999, #46045.
`node:vm`, native addons, workers: #46129, #46198, #45615, #33412,
#38031.
Dev server: #42790, #42850, #39488.
Other: #35991, #38030, #38586, #40526, #41830, #46029, #46078, #46114,
and the bug of #42215.

#### Behavior changes

- `crypto.randomFill(view, cb)` reports `ERR_CRYPTO_OPERATION_FAILED` if
it could not fill the view. It used to report success.
- A function registered twice on `process` runs twice, as in Node.
(#41830)
- Under `--hot`, a reload after an uncaught exception cancels the timers
that the failed run created. The new run's timers fire, and so do timers
and `fs.watchFile` watchers that earlier runs kept on `globalThis`. The
reload also resets the exit code to 0. (#38206)
- The parent of a Worker no longer gets an `'error'` event for a failed
`Bun.serve` request in that Worker. The error is printed, as on the main
thread.
- `hash.write()` after `digest()` drops the chunk, as in Node. (#35991)
- `http2` over TLS with `server.emit("connection", socket)`:
`session.socket` is a `TLSSocket` and a `'connection'` listener fires,
as in Node. (#38124)
- A `slice()` with no type of a body `Blob` has `type === ""`, as in
Node. (#46114)
- `util.types.isProxy(globalThis)` is false, as in Node.
- A write on a `node:net` or `node:tls` socket that is still pending
when the connection closes calls back with `ECANCELED`, as in Node.
- `HTMLRewriter.on()` throws for a selector that is nested too deeply.
- `Bun.Image`: `maxPixels` also bounds PNG metadata, `metadata()` of a
GIF reports the size that gets decoded, and a path with a NUL byte
throws. (#40526)
- `bun build --no-bundle` with the `base64` or `dataurl` loader prints
an error. (#36334)
- Bundled output for a default-exported function or class expression
changes to `var file_default = function name() {}`, as in esbuild.
`.name` is unchanged. (#46097)
- A Proxy in the prototype chain of a `node:vm` global is allowed. jsdom
30 with `runScripts` needs this.
- `bun:sqlite` commits a write that `iterate()` left open when the main
thread exits. An explicit `BEGIN` is still rolled back.
- When a Worker stops while a native addon's async work is still
running, the addon's finalizers run after that work is done, as in Node.
- Shell: `mv -n` and `cp -n` don't overwrite, and `cp` reads `R`, `v`
and `n` anywhere in a flag group.

#### Performance

- Ordinary `node:vm` contexts run at the same speed. With
`DONT_CONTEXTIFY`, stores to globals and reads of inherited names in a
hot loop cost 10 to 20 % more than on `main`.
- A `process.env` write costs 20 to 35 % less than after #46021.
- An async read into a `WebAssembly.Memory` view looks through the live
memories: about 1.5 µs more with one memory.

#### Not included

- Taken in part, so these should stay open for the rest: #36334, #38974,
#39488, #40526, #40769, #42485, #45615, #45999, #46029, #46092, #46114,
#46168, #46207, #46239.
- Needs a WebKit change: #34655 #39204 #40170 #40253 #41587 #41900
#42699 #42897 #42903 #43084 #43813 #44518 #44949 #45770 #45804 #45827.
- #44723 (covered by #46097), #41675, #42753, #38762, #41475, #46016.

### How did you verify your code works?

- Every item was reproduced first on release builds of `main`, of its
parent and of 1.4.3, and compared with Node v26 where Node has the API.
- Every fix has a test that fails on `main` and passes on a debug + ASAN
build of this branch, except where that isn't practical (huge inputs,
code that only runs on Windows or macOS).
- The regressions were checked on grids of calls against the build
before #46021 and against 1.4.3.
- Bun's own test suites and Node's `test-vm-*`, `test-http2-*` and
`test-zlib-*` files for the areas touched.

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.

1 participant