Skip to content

Node-API finalizer that calls into JS panics the process (Invoke in DisallowJavascriptExecutionScope) #36568

Description

@hexbinoct

Version: Deno 2.9.5 (also reproduces on canary 2.9.5+33ff918)

A native addon whose Node-API finalizer calls back into JavaScript takes down the whole process with a Rust panic instead of running the callback. Node-API finalizers with the plain napi_finalize signature are allowed to call into JS; Node runs them outside the GC pass at a point where the engine can execute JavaScript. Deno invokes them from V8's weak callback, where JS execution is disallowed, so the first napi_call_function inside a finalizer trips a V8 fatal error, which cli/lib.rs turns into a panic and the process aborts. The finalizer machinery is in ext/napi/js_native_api.rs (Reference::weak_callback calls finalize_cb directly).

Minimal repro, one C file plus a driver:

addon.c

#include <node_api.h>

static int dummy;

static void finalize_cb(napi_env env, void* data, void* hint) {
  napi_ref cb_ref = (napi_ref)hint;
  napi_value cb, global;
  napi_get_reference_value(env, cb_ref, &cb);
  napi_get_global(env, &global);
  napi_call_function(env, global, cb, 0, NULL, NULL);
  napi_delete_reference(env, cb_ref);
}

static napi_value create(napi_env env, napi_callback_info info) {
  size_t argc = 1;
  napi_value js_cb;
  napi_get_cb_info(env, info, &argc, &js_cb, NULL, NULL);
  napi_ref cb_ref;
  napi_create_reference(env, js_cb, 1, &cb_ref);
  napi_value ext;
  napi_create_external(env, &dummy, finalize_cb, cb_ref, &ext);
  return ext;
}

NAPI_MODULE_INIT() {
  napi_value fn;
  napi_create_function(env, "create", NAPI_AUTO_LENGTH, create, NULL, &fn);
  napi_set_named_property(env, exports, "create", fn);
  return exports;
}

repro1.cjs

const addon = require('./repro1.node');

(function make() {
  addon.create(() => console.log('JS finalizer callback ran'));
})();

gc();
setTimeout(() => {
  gc();
  setTimeout(() => console.log('exit ok'), 100);
}, 100);

Build and run (Linux, headers from a Node install, here the node:24-bookworm Docker image):

gcc -shared -fPIC -o repro1.node addon.c -I/usr/local/include/node
node --expose-gc repro1.cjs
deno run -A --v8-flags=--expose-gc repro1.cjs

Node v24.18.1:

JS finalizer callback ran
exit ok

Deno 2.9.5 (exit code 1):

============================================================
Deno has panicked. This is a bug in Deno. Please report this
at https://github.com/denoland/deno/issues/new.
If you can reliably reproduce this panic, include the
reproduction steps and the stack trace URL below in your report.

Platform: linux x86_64
Version: 2.9.5
Args: ["/opt/deno-stable/deno", "run", "-A", "--v8-flags=--expose-gc", "repro1.cjs"]

View stack trace at:
https://panic.deno.com/v2.9.5/x86_64-unknown-linux-gnu/k6m9xF-19m1D0r9m1Dik9m1Dwj9m1Dqgu-0Dy0qn1Doi1vxCs7p1_Boqy1_Bkxx1_Bkq29xF0rz9xF22y9xF6r22lCi1z2lCimj51C6x841Ck-m1_Bgv36hCio36hCs326hC8j16hCw7u6hCw-s6hCw5n6hCo2l6hCko-ygCi94ygCijtl9B8lhl9Bq98k9Bw6gg8Bum2_7B4l2_7Bikw_7B

thread 'main' (160) panicked at cli/lib.rs:698:5:
Fatal error in :0: Invoke in DisallowJavascriptExecutionScope
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Canary 2.9.5+33ff918 panics the same way at cli/lib.rs:709:5.

Tested on:

  • Linux x86_64 (Debian bookworm, Docker): Deno 2.9.5 stable and canary 2.9.5+33ff918, both panic; Node v24.18.1 runs the callback and exits 0.
  • Windows 11 x86_64: Deno 2.9.5 panics identically (cli\lib.rs:698:5, same fatal message). On Windows the addon was built with MSVC as part of the test suite below, and loading it in Deno currently requires the deno executable to be copied to a file named node.exe so the DLL import resolves; that loader quirk is unrelated to this bug.

Expected: either run the finalizer at a point where JS execution is legal, as Node does, or fail with an error. A process-fatal panic means any npm package whose finalizer touches JS kills Deno outright, and the docs list Node-API addons as supported (https://docs.deno.com/runtime/fundamentals/node/).

This surfaced while running the Node-API conformance test suite (https://github.com/nodejs/node-api-cts) against Deno; js-native-api/test_reference/test_finalizer.js and js-native-api/test_function/test.js both crash on it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions