Skip to content

napi: clear pending exceptions between finalizers during env cleanup#30291

Open
robobun wants to merge 15 commits into
mainfrom
farm/61ff913a/fix-napi-create-error-during-finalizer-cleanup
Open

napi: clear pending exceptions between finalizers during env cleanup#30291
robobun wants to merge 15 commits into
mainfrom
farm/61ff913a/fix-napi-create-error-during-finalizer-cleanup

Conversation

@robobun

@robobun robobun commented May 5, 2026

Copy link
Copy Markdown
Collaborator

Closes #30286.
Fixes #19917.
Fixes #26081.
Fixes #28557.

Repro

gitnexus analyze on a synthetic repo with 15 trivial Java files reliably panicked at worker exit:

panic: NAPI FATAL ERROR: Error::New napi_create_error
  Bun::NapiFinalizer::call         napi_finalizer.cpp:13
  Zig::NapiRef::callFinalizer       napi.h:742
  NapiEnv::BoundFinalizer::call     napi.h:460
  NapiEnv::cleanup                  napi.h:211
  napi_internal_cleanup_env_cpp     napi.cpp:2993
  web_worker.exitAndDeinit ...

Cause

Two interacting bugs:

  1. NapiEnv::cleanup did not clear pending exceptions between finalizers. If finalizer N leaked a JSC VM exception (a common thing when a finalizer calls into JS via napi_call_function, or internally hits an allocation failure), finalizer N+1 inherited it. Node.js does the opposite — finalizers run with no JS frame above them to catch, so each one starts from a clean exception state.

  2. createErrorWithNapiValues refused to run with a pending VM exception. Node.js's napi_create_error is a pure value-producing function with no such check. Because Bun's napi_is_exception_pending deliberately skips the VM-scope check during cleanup, node-addon-api's Error::New(env) helper saw "no pending exception", took the "create a new error" branch, got back napi_pending_exception from createErrorWithNapiValues, and NAPI_FATAL_IF_FAILED fired napi_fatal_error.

Layer 1 also surfaces as panic: A C++ exception occurred during process teardown: a finalizer's leaked exception unwinds out of NapiEnv::cleanupnapi_internal_cleanup_env_cppVirtualMachine.onExit / globalExit and hits std::set_terminate. That's the stack on #19917, #26081, and #28557 — all NAPI modules (@huggingface/transformers / onnxruntime-node, bun:sqlite).

Fix

  • NapiEnv::cleanup: clear VM and env pending exceptions at the start of cleanup, after the cleanup-hook drain, after each finalizer, and after the instance-data finalizer via a new clearExceptionsBetweenFinalizers helper. Uses DECLARE_TOP_EXCEPTION_SCOPE(vm).clearException() so the EXCEPTION_SCOPE_VERIFICATION bookkeeping gets reset too — leaving it uncleared would trip debug asserts in the next finalizer's napi calls. The start/after-drain clears cover the case where the exception predates the finalizer phase (a cleanup hook or a terminated Worker's teardown promoted a scheduled exception onto the VM), which is the node-canvas shape reported in the issue thread (panic: NAPI FATAL ERROR: Error::Error napi_create_object with canvas@3.2.1 + worker.terminate()).
  • createErrorWithNapiValues: drop the DECLARE_THROW_SCOPE + RETURN_IF_EXCEPTION at entry. The string inputs are already validated as strings before getString() is called, so nothing in the body can throw.

Verification

New test/napi/napi-app/test_finalizer_create_error.c addon, driven by test/napi/napi.test.ts:

  • Wraps two JS objects.
  • First-to-finalize finalizer calls a throwing JS function via napi_call_function, leaves the VM exception pending.
  • Second-to-finalize finalizer calls napi_create_error and prints the returned status.

Before the patch: status is 10 (napi_pending_exception) and debug ASAN aborts on the leaked exception first. After the patch: status is 0 (napi_ok) and the process exits 0.

A second test covers the pre-finalizer-phase leak: the addon registers a cleanup hook that schedules an exception via napi_throw_error and promotes it onto the VM through napi_call_function's prologue, then the wrap finalizer probes napi_create_error. Fails without the start/after-drain clears, passes with them.

Gate: bun bd test test/napi/napi.test.ts -t "30286" passes with the fix, fails (either an ASAN assertion abort on debug, or a non-zero status on release) without it.

Also verified the canvas@3.2.1 + worker.terminate() repro from the issue thread: panics on the unfixed build, exits 0 with this PR.

Related

@robobun

robobun commented May 5, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 6:40 PM PT - Jun 9th, 2026

@robobun, your commit 4612807 is building: #61670

@github-actions github-actions Bot added the claude label May 5, 2026
@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Adds VM-level exception clearing during N-API environment teardown and removes throw-scope reliance when creating errors. Introduces a clearExceptionsBetweenFinalizers helper, invokes it between finalizers in NapiEnv::cleanup, includes TopExceptionScope, and adds a regression N-API test and build target validating napi_create_error during cleanup.

Changes

NAPI Finalizer Exception Handling

Layer / File(s) Summary
VM Exception helper / include
src/jsc/bindings/napi.cpp
Adds #include <JavaScriptCore/TopExceptionScope.h> and defines void NapiEnv::clearExceptionsBetweenFinalizers() which calls DECLARE_TOP_EXCEPTION_SCOPE(m_vm).clearException() and clears m_pendingException.
Core behavior: error creation & pending check
src/jsc/bindings/napi.cpp
createErrorWithNapiValues no longer creates a throw scope; it obtains code/message strings via non-throwing getString and constructs the Error via createErrorWithCode without throw-scope bookkeeping. napi_is_exception_pending is updated to use DECLARE_TOP_EXCEPTION_SCOPE(vm) when inspecting VM exceptions during cleanup.
Cleanup wiring
src/jsc/bindings/napi.h
NapiEnv::cleanup() updated to call clearExceptionsBetweenFinalizers() after each finalizer invocation and after running/clearing instanceDataFinalizer() (helper declared in NapiEnv; defined out-of-line to confine TopExceptionScope usage).
Build target
test/napi/napi-app/binding.gyp
Adds new target test_finalizer_create_error to build the regression native addon with the same include/dependency settings and N-API flags used by other tests.
Regression addon & test
test/napi/napi-app/test_finalizer_create_error.c, test/napi/napi.test.ts
Adds native test module that installs two finalizers (one that leaves a pending VM exception via napi_call_function, then one that calls napi_create_error and prints create_error_status=<n>), and a JS test that runs the addon and asserts create_error_status=0, exit code 0, and empty stderr.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the primary change: clearing pending exceptions between finalizers during environment cleanup, which is the main technical fix addressing the crash.
Linked Issues check ✅ Passed The PR successfully addresses all coding requirements from linked issues: clearing VM/env exceptions between finalizers (#30286), fixing crash during worker cleanup (#19917, #26081, #28557), and ensuring napi_create_error is safe during cleanup (supersedes #28248, #29785).
Out of Scope Changes check ✅ Passed All changes are in-scope: napi.cpp/napi.h modifications implement the core fix, test_finalizer_create_error.c/binding.gyp add regression test infrastructure, and napi.test.ts adds the verification test—all directly supporting the finalizer cleanup fix.
Description check ✅ Passed The pull request description is comprehensive and well-structured, providing clear sections on repro, cause, fix, and verification.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/napi/napi.test.ts`:
- Around line 720-721: Remove the negative assertions that check stderr for
"panic" and "NAPI FATAL" and rely on the existing positive assertions (the
create_error_status=0 and exitCode=0 checks) to validate the test; specifically
delete or comment out the two lines using expect(stderr).not.toContain("panic")
and expect(stderr).not.toContain("NAPI FATAL") (or convert them to non-failing
diagnostic logs) so the test follows the guideline of asserting success rather
than absence of panic strings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 689b52ca-94bc-4d1e-940d-78eb94b9b3cc

📥 Commits

Reviewing files that changed from the base of the PR and between b009453 and 6de6df8.

📒 Files selected for processing (5)
  • src/jsc/bindings/napi.cpp
  • src/jsc/bindings/napi.h
  • test/napi/napi-app/binding.gyp
  • test/napi/napi-app/test_finalizer_create_error.c
  • test/napi/napi.test.ts

Comment thread test/napi/napi.test.ts Outdated
@github-actions

github-actions Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Found 5 issues this PR may fix:

  1. panic: Error::New napi_get_last_error_info #20663 - Panic in Error::New via napi_get_last_error_info — exact same createErrorWithNapiValues crash path this PR fixes
  2. bun test panics after tests pass when using mock.module() with bun:sqlite and repeated SQLite create/close cycles on macOS #28557 - "A C++ exception occurred" panic during process teardown after tests pass with NAPI modules (bun:sqlite + mock.module)
  3. Bun test crashes when using @huggingface/transformers #26081 - "A C++ exception occurred" during exit after tests pass with @huggingface/transformers ONNX NAPI module
  4. Bun v1.2.14 (6a363a38) on macOS Silicon crashes with the message "A C++ exception occurred" when attempting to use the huggingface/transformers pipeline for "feature-extraction". #19917 - "A C++ exception occurred" during process exit with @huggingface/transformers NAPI module loaded
  5. npx bun test crash #21068 - "A C++ exception occurred" panic during bun test with NAPI module loaded via process_dlopen

If this is helpful, copy the block below into the PR description to auto-close these issues on merge.

Fixes #20663
Fixes #28557
Fixes #26081
Fixes #19917
Fixes #21068

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. fix(napi): make napi_create_error succeed when pending exception exists #29785 - Also fixes createErrorWithNapiValues rejecting calls when a pending exception exists in napi.cpp
  2. fix(napi): don't fail napi_create_error when a pending exception exists #28248 - Earlier attempt at the same napi_create_error pending-exception fix in napi.cpp

🤖 Generated with Claude Code

Comment thread test/napi/napi.test.ts Outdated
robobun added a commit that referenced this pull request May 5, 2026
Per CLAUDE.md: 'Never write tests that check for no panic or uncaught
exception in the test output. These tests will never fail in CI.'

The positive assertions (stdout == 'create_error_status=0' and
exitCode == 0) already fail decisively if the finalizer panics --
stdout is empty on a crash. Also added stderr == '' as a positive
absence-of-diagnostic check.

Addresses CodeRabbit review on #30291.

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

Thanks for addressing the test-assertion nit. No bugs found, but this changes JSC exception-scope handling in the NAPI cleanup path (dropping the throw scope in createErrorWithNapiValues and clearing VM exceptions between finalizers), so it'd be good to have someone with JSC internals context confirm the scope semantics before merge.

Extended reasoning...

Overview

This PR touches two C++ files in the NAPI bindings (src/jsc/bindings/napi.cpp, src/jsc/bindings/napi.h) plus a new C test addon, a binding.gyp target, and a regression test in napi.test.ts. The functional changes are: (1) createErrorWithNapiValues drops its DECLARE_THROW_SCOPE and three RETURN_IF_EXCEPTION guards so napi_create_error succeeds even with a pending VM exception, matching Node.js; and (2) NapiEnv::cleanup() now calls a new clearExceptionsBetweenFinalizers() helper (which uses DECLARE_TOP_EXCEPTION_SCOPE(vm).clearException() and clears m_pendingException) after each finalizer and after the instance-data finalizer.

Security risks

None apparent — this is internal exception-state hygiene during env teardown, not an auth/input-validation surface.

Level of scrutiny

High. This is production-critical native-bindings code on the worker/process teardown path, and the change alters JSC exception-scope semantics. Two specific points deserve a JSC-aware reviewer's eye: (a) the claim that JSValue::getString(globalObject) on a value already verified isString() can never throw (rope resolution / OOM edge cases) and that createErrorWithCode is safe without a surrounding throw scope under EXCEPTION_SCOPE_VERIFICATION; and (b) that DECLARE_TOP_EXCEPTION_SCOPE(m_vm).clearException() on a temporary is the right idiom here vs. a longer-lived scope or a catch scope. The reasoning in the PR description is thorough and plausible, but these are exactly the kind of invariants that are easy to get subtly wrong.

Other factors

The PR includes a targeted regression test that exercises both layers of the fix, and the author already addressed my earlier style nit (commit eddff53 swapped the negative panic-string assertions for expect(stderr).toBe("")). The PR also claims to close several long-standing crash reports (#19917, #26081, #28557), which raises the impact of getting the fix right. No CODEOWNERS cover these files. Given the subtlety of JSC exception-scope bookkeeping and the breadth of the claimed fixes, I'm deferring rather than approving.

Comment thread src/jsc/bindings/napi.h
Comment thread src/jsc/bindings/napi.cpp Outdated
@robobun

robobun commented May 9, 2026

Copy link
Copy Markdown
Collaborator Author

Also fixes #30431 — the macOS shutdown crash there (panic(main thread): A C++ exception occurred after all tests pass on onnxruntime-node) is the same layer-1 exception-leak path this PR fixes. The Linux Segmentation fault at address 0x1A half of that issue was a separate, already-merged fix (#29981 / 65a0b2f7f6, re-entrant napi_module_register iterator invalidation).

@HaleTom

HaleTom commented May 9, 2026

Copy link
Copy Markdown

@dylan-conway @Jarred-Sumner this PR needs a maintainer to unblock CI and merge.

The only failing check is "Lint JavaScript" — 3 oxlint Unsafe declaration merging between classes and interfaces errors in src/js/. The PR only touches C++ (napi.cpp, napi.h, napi_finalizer.cpp) and test files — zero changes to src/js/. I verified locally: the PR's merge ref passes bun lint with 0 warnings, 0 errors. This is a stale CI run against an old merge base (the base has moved forward since the run was triggered).

Could someone please re-run ?

robobun and others added 9 commits June 2, 2026 05:37
Tree-sitter inside node:worker_threads deterministically panicked at
worker exit once ~15 finalizers had run:

  panic: NAPI FATAL ERROR: Error::New napi_create_error
  at NapiEnv::cleanup -> NapiRef::callFinalizer -> NapiFinalizer::call

Two things were broken. First, NapiEnv::cleanup ran each finalizer
without clearing any pending exception after it returned, so a throw
from one finalizer leaked into the next. Node.js doesn't do that --
finalizers run with no JS frame above them to catch, so propagating
the exception is pointless and only confuses later finalizers.

Second, createErrorWithNapiValues refused to produce an Error value
while a VM-level exception was pending (DECLARE_THROW_SCOPE +
RETURN_IF_EXCEPTION at the top). Node.js's napi_create_error has no
such check -- it is a pure value-producing function. Because
napi_is_exception_pending deliberately skips the VM-scope check
during cleanup for safety, node-addon-api's Error::New(env) saw 'no
pending exception', fell through to napi_create_error, got back
napi_pending_exception, and tripped NAPI_FATAL_IF_FAILED ->
napi_fatal_error.

Fix both layers: clear env/VM exceptions after each finalizer in
NapiEnv::cleanup, and drop the pending-exception check from
createErrorWithNapiValues. Matches Node.js on both counts.

Regression test: a standalone addon wraps two JS objects; the first
finalizer calls a throwing JS function via napi_call_function
(leaving the JSC VM exception pending without clearing it), the
second calls napi_create_error and prints the returned status.
Before the fix the status is 10 (napi_pending_exception) and the
debug ASAN build aborts on the leaked exception first; after the
fix the status is 0 (napi_ok) and the process exits cleanly.

Closes #30286.
Related: #22259, #28248, #29785.
Per CLAUDE.md: 'Never write tests that check for no panic or uncaught
exception in the test output. These tests will never fail in CI.'

The positive assertions (stdout == 'create_error_status=0' and
exitCode == 0) already fail decisively if the finalizer panics --
stdout is empty on a crash. Also added stderr == '' as a positive
absence-of-diagnostic check.

Addresses CodeRabbit review on #30291.
Declared inline in napi.h it was getting instantiated in every TU that
pulled in napi_external.h. On Windows aarch64 (build 51887's only
real failure), the TopExceptionScope ctor/dtor are JS_EXPORT_PRIVATE
under ENABLE_EXCEPTION_SCOPE_VERIFICATION; that broke build-cpp.
Confining the body to napi.cpp means TopExceptionScope is touched
from exactly one TU, matching how every other caller
(ErrorStackTrace.cpp, NodeVMModule.cpp, MessagePort.cpp, ...) uses
it today.
@robobun robobun force-pushed the farm/61ff913a/fix-napi-create-error-during-finalizer-cleanup branch from 9cacea6 to 720f084 Compare June 2, 2026 05:41
@robobun

robobun commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator Author

CI note: build #59847 failed only on `debian-13-x64-test-bun` (1 shard of 17) — the failing test is `napi_get_value_string_utf8 with buffer > copies auto len`, which passes `424242` as the buffer size and trips a debug-mode Rust UB check (`slice::from_raw_parts_mut requires the pointer to be aligned and non-null`).

This is pre-existing on main and unrelated to this PR: I reproduced it on a clean checkout of main at f58d14649c (no changes from this branch) and it fails identically. This PR only touches napi_create_error / NapiEnv::cleanup (finalizer exception handling) — it does not touch napi_get_value_string_utf8.

The PR diff itself is green. This needs a maintainer to merge (the unrelated pre-existing failure will clear on its own or needs a separate fix).

ualtinok added a commit to cortexkit/magic-context that referenced this pull request Jun 3, 2026
Wire the OpenCode 'server.instance.disposed' SDK event to stop this instance's
process-resident resources (RPC server, dream-schedule timer registration,
auto-update abort controller). Desktop runs multiple plugin instances in one
process, each disposed independently, so cleanup is gated on the disposed
directory resolving to our own project identity — never tears down a sibling
instance's still-live resources. Also fixes a pre-existing leak: the dream-timer
registration cleanup was never captured.

Deliberately does NOT dispose the native ONNX embedding session here — forcing
onnxruntime-node's destructor on teardown makes the Bun N-API exit crash (#95)
worse, not better (tracked upstream at oven-sh/bun#30291); the OS reclaims that
memory on exit. Pi keeps its own session_shutdown cleanup (no server.instance.
disposed event in Pi) — documented divergence.

Co-authored-by: Alfonso [Magic Context] <288211368+alfonso-magic-context@users.noreply.github.com>
@basedcorp99

Copy link
Copy Markdown

Additional confirmation that this still affects Bun 1.3.14 with native N-API modules in workers, using canvas@3.2.1.

Minimal repro:

// worker-canvas.mjs
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const { createCanvas } = require("canvas");
const canvas = createCanvas(16, 16);
canvas.getContext("2d").fillRect(0, 0, 16, 16);
postMessage({ ok: true, bytes: canvas.toBuffer("image/png").length });
// main-worker-canvas.mjs
const worker = new Worker(new URL("./worker-canvas.mjs", import.meta.url).href, { type: "module" });
worker.onmessage = event => {
  console.log(JSON.stringify(event.data));
  worker.terminate();
};
worker.onerror = event => {
  console.error("worker error", event.message || event);
  worker.terminate();
  process.exit(1);
};
npm i canvas@3.2.1
bun main-worker-canvas.mjs

Observed:

If the worker instead receives a message and calls parentPort.close() from inside the worker before process exit, the same canvas work exits cleanly in my smoke test. That lines up with this PR's finalizer-cleanup diagnosis.

robobun added 3 commits June 8, 2026 00:07
A native teardown callback can leave a pending VM exception before the
first wrap finalizer runs: a terminated Worker's teardown promotes a
scheduled exception onto the VM (napi_call_function's prologue calls
throwPendingException before validating arguments), and nothing cleared
it between the cleanup-hook phase and the finalizer phase. The first
finalizer's first napi call then failed with napi_pending_exception and
node-addon-api escalated to napi_fatal_error -- the node-canvas shape
of #30286:

  panic: NAPI FATAL ERROR: Error::Error napi_create_object

reported with canvas@3.2.1 + worker.terminate(). Clear pending
exceptions at the start of cleanup and again after the cleanup-hook
drain so every native teardown callback starts from a clean exception
state, completing the invariant the between-finalizer clearing already
established.

Deterministic regression test: the addon registers a cleanup hook that
schedules an exception via napi_throw_error and promotes it with
napi_call_function, then a wrap finalizer probes napi_create_error.
Fails without this commit (status -110 / abort), passes with it.
Verified the canvas@3.2.1 worker repro from the issue thread exits 0.
Comment thread src/jsc/bindings/napi.h
robobun and others added 2 commits June 8, 2026 00:27
drain() ran hooks back-to-back, so a hook that leaked a VM exception
poisoned the next hook's napi calls. Apply the same per-callback clear
the finalizer loop uses, making cleanup()'s documented invariant
(every native teardown callback starts from a clean exception state)
hold for hooks as well.
@HaleTom

HaleTom commented Jun 10, 2026

Copy link
Copy Markdown

Thanks @basedcorp99 for drawing my attention back to this PR.

@dylan-conway @Jarred-Sumner robobun says the PR itself is green -- would you please take a look?

Cheers!

@robobun

robobun commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator Author

Independent confirmation of this PR's analysis, from triaging the same Sentry crash (BUN-2ZQ9 / BUN-38NS, NAPI FATAL ERROR: Error::New napi_create_error, ~23/day on 1.3.14). I reproduced it on 1.4.0 with a raw-C addon that replays node-addon-api's Error::New(env) call sequence from a napi_wrap finalizer during env cleanup, in two states: after worker.terminate() landed mid-execution (thrown TerminationException left pending), and with an exception leaked by an env cleanup hook. Node 24.3.0 returns napi_ok for every call in both states; bun fails napi_create_error with napi_pending_exception while napi_is_exception_pending reports false, which is exactly the mismatch NAPI_FATAL_IF_FAILED turns into the abort.

While verifying the fix against Node I found the same class extends past createErrorWithNapiValues, so the abort comes back one call later once that function is fixed:

  • napi_create_reference, napi_get_reference_value, napi_reference_ref, napi_reference_unref still use NAPI_PREAMBLE. Node omits it for the whole reference family ("V8 calls here cannot throw JS exceptions"). node-addon-api's Error::Error stores the new error in a reference and ThrowAsJavaScriptException reads it back, each with its own NAPI_FATAL_IF_FAILED, so with only the create-error fix the crash moves to Error::Error napi_create_reference.
  • napi_create_string_latin1 fails with a pending exception even in release builds (BunString__toJS has RETURN_IF_EXCEPTION), and napi_create_string_utf8 with a non-ASCII message trips EXCEPTION_ASSERT/assertNoException in BunString__createUTF8ForJS on debug builds. The between-finalizer clearing in this PR masks both inside NapiEnv::cleanup, but they stay broken in other pending-exception contexts (Node allows these calls anywhere).
  • For the worker case, clearing at cleanup entry happens after Process__dispatchOnExit, so the pending TerminationException still skips the worker's process.on('exit') handlers (dispatchExitInternal bails on hasExceptionsAfterHandlingTraps), and any DeferTermination scope entered in that window trips ASSERT(vm.hasTerminationRequest()) in VMTraps::deferTerminationSlow on debug builds, because worker shutdown cleared the request bit but not the thrown exception. Switching web_worker.rs shutdown from clear_has_termination_request() to the existing JSGlobalObject__clearTerminationException helper (already used by the REPL and test runner for exactly this) fixes the state at the source.

Branch with those fixes plus tests comparing against Node for all of the above: https://github.com/oven-sh/bun/branches/all?query=farm/0a365ff8/napi-error-new-finalizers (farm/0a365ff8/napi-error-new-finalizers, f0409d5). The fail-before proof: on an unfixed debug build the new test aborts on the asserts above; on an unfixed release build it gets create_error=10 create_string_latin1=9. Happy for any of it to be folded in here, or rebased as a follow-up once this lands; not opening a separate PR since this one already covers the headline crash.

@HaleTom

HaleTom commented Jun 11, 2026

Copy link
Copy Markdown

@robobun I'm a bit maxxed out ATM and my agent isn't as good as you are.

I'll take the W on the headline fix and give you the pleasure of committing your own work :)

@HaleTom

HaleTom commented Jun 14, 2026

Copy link
Copy Markdown

@dylan-conway @Jarred-Sumner could you please unleash robobun to merge this?

@robobun said:

The PR diff itself is green. This needs a maintainer to merge (the unrelated pre-existing failure will clear on its own or needs a separate fix)

@robobun

robobun commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator Author

Appreciate the vote of confidence, @HaleTom! I don't have merge rights on this repo (no maintainer/admin bit), and the PR currently shows BLOCKED under branch protection, so the merge button genuinely needs a maintainer.

Everything on my side is ready: diff is green, main is merged in, all review threads resolved, regression tests + the canvas@3.2.1 worker-terminate repro verified. Over to @dylan-conway / @Jarred-Sumner whenever you have a moment.

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

Labels

Projects

None yet

3 participants