Skip to content

Interaction between a callback called with MakeCallback and Promises on the Node.js worker thread crashes with FATAL ERROR: Error::Error napi_define_properties #1681

@JCMais

Description

@JCMais

I found this while trying to migrate my addon to N-API. It uses libuv to call a function either due to events on a socket or due to a timeout. I was able to reproduce this with just a single timer on libuv, the code is available here: https://github.com/JCMais/node-addon-promise-fatal-error

The basic gist of it is that if I do:

// ... snip

function workerMain() {
  new Promise((resolve, reject) => {

    let resolveInner
    const innerPromise = new Promise((r) => {
      resolveInner = r
    })

    addon.onMessage(() => {
      resolveInner();
    })

    innerPromise.then(resolve).catch(reject)
  })
}

The Node.js process crashes at some point after the callback is called on the JS side. This only happens if I call the callback using MakeCallback, it does not matter if I pass an async context or not.

If I call it using Call, it works fine.

It also only crashes if the inner promise is resolved from within the callback, if I do something like this instead:

// ... snip

function workerMain() {
  new Promise((resolve, reject) => {

    let resolveInner
    const innerPromise = new Promise((r) => {
      resolveInner = r
    })

    addon.onMessage(() => {
      // ... noop
    })

    setTimeout(resolveInner, 3000);

    innerPromise.then(resolve).catch(reject)
  })
}

It works fine.

This was tested using Node.js v20.18.1, and n-api v8.5.0.

Any ideas on what I might be doing wrong are appreciated. :)

Here is the full stack trace:

* thread #8, stop reason = signal SIGABRT
    frame #0: 0x00000001982555d0 libsystem_kernel.dylib`__pthread_kill + 8
libsystem_kernel.dylib`__pthread_kill:
->  0x1982555d0 <+8>:  b.lo   0x1982555f0    ; <+40>
    0x1982555d4 <+12>: pacibsp 
    0x1982555d8 <+16>: stp    x29, x30, [sp, #-0x10]!
    0x1982555dc <+20>: mov    x29, sp
Target 0: (node) stopped.
(lldb) v8 bt
 * thread #8: tid = 0x11c5231, 0x00000001982555d0 libsystem_kernel.dylib`__pthread_kill + 8, stop reason = signal SIGABRT
  * frame #0: 0x00000001982555d0 libsystem_kernel.dylib`__pthread_kill + 8
    frame #1: 0x000000019828dc20 libsystem_pthread.dylib`pthread_kill + 288
    frame #2: 0x000000019819aa30 libsystem_c.dylib`abort + 180
    frame #3: 0x0000000100220f28 node`node::OnFatalError(location="Error::Error", message="napi_define_properties") at node_errors.cc:586:3
    frame #4: 0x0000000100195704 node`napi_fatal_error(location="Error::Error", location_len=18446744073709551615, message="napi_define_properties", message_len=18446744073709551615) at node_api.cc:874:3
    frame #5: 0x00000001072db17c addon.node`Napi::Error::Fatal(location="Error::Error", message="napi_define_properties") at napi-inl.h:3173:3
    frame #6: 0x00000001072db394 addon.node`Napi::Error::Error(this=0x000000011e404450, env=0x000000011e006550, value=0x000000012800e028) at napi-inl.h:3222:7
    frame #7: 0x00000001072db1a8 addon.node`Napi::Error::Error(this=0x000000011e404450, env=0x000000011e006550, value=0x000000012800e028) at napi-inl.h:3179:37
    frame #8: 0x00000001072dada4 addon.node`Napi::Error::New(env=0x000000011e006550) at napi-inl.h:3158:10
    frame #9: 0x00000001072dfa68 addon.node`Napi::Function::MakeCallback(this=0x0000000172a42308, recv=0x000000012800e008, argc=1, args=0x0000000172a42380, context=0x000000011e004b00) const at napi-inl.h:2752:3
    frame #10: 0x00000001072de950 addon.node`Napi::Function::MakeCallback(this=0x0000000172a42308, recv=0x000000012800e008, args=0x0000000172a422d8, context=0x000000011e004b00) const at napi-inl.h:2734:10
    frame #11: 0x00000001072de4f4 addon.node`ReproduceIssue::ReproduceIssue::CallOnMessageCallback(this=0x000000011e009040) at ReproduceIssue.cc:171:16
    frame #12: 0x00000001072dd0f0 addon.node`ReproduceIssue::ReproduceIssue::OnTimeout(timer=0x000000011e009088) at ReproduceIssue.cc:188:8
    frame #13: 0x00000001015b207c node`uv__run_timers(loop=0x0000000172a42ae0) at timer.c:178:5 [opt]
    frame #14: 0x00000001015b5840 node`uv_run(loop=0x0000000172a42ae0, mode=UV_RUN_DEFAULT) at core.c:465:5 [opt]
    frame #15: 0x0000000100008544 node`node::SpinEventLoopInternal(env=0x0000000128010000) at embed_helpers.cc:41:7
    frame #16: 0x00000001003cd3a8 node`node::worker::Worker::Run(this=0x000000011cf075e0) at node_worker.cc:408:35
    frame #17: 0x00000001003d15a8 node`node::worker::Worker::StartThread(v8::FunctionCallbackInfo<v8::Value> const&)::$_0::operator()(this=0x0000000172a42fb7, arg=0x000000011cf075e0) const at node_worker.cc:739:8
    frame #18: 0x00000001003d1554 node`node::worker::Worker::StartThread(v8::FunctionCallbackInfo<v8::Value> const&)::$_0::__invoke(arg=0x000000011cf075e0) at node_worker.cc:728:55
    frame #19: 0x000000019828df94 libsystem_pthread.dylib`_pthread_start + 136

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Need Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions