Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
7b0700038d Fix NAPI crash when process.exit() is called during native module cleanup
Fixes oven-sh/bun#24054

When `process.exit()` was called from within a N-API module callback (like
sqlite3's query callback), the NAPI finalizers would attempt to create JavaScript
errors during VM termination, causing a fatal crash:
"NAPI FATAL ERROR: Error::New napi_create_error"

The fix:

1. Skip calling NAPI finalizers when the VM is terminating, as they may try
   to perform JavaScript operations that are no longer allowed

2. Return `napi_cannot_run_js` when NAPI error creation functions are called
   during VM termination, instead of attempting to create errors

This prevents native modules from crashing Bun when cleaning up during
process.exit().

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 07:06:27 +00:00
2 changed files with 18 additions and 0 deletions

View File

@@ -114,6 +114,14 @@ using namespace Zig;
(_env)->checkGC(); \
} while (0)
// Check if the VM is terminating and return early if so
#define NAPI_CHECK_VM_NOT_TERMINATING(_env) \
do { \
if ((_env)->isVMTerminating()) { \
return napi_set_last_error(_env, napi_cannot_run_js); \
} \
} while (0)
// Return the specified code if condition is false. Only use for input validation.
#define NAPI_RETURN_EARLY_IF_FALSE(_env, condition, code) \
do { \
@@ -1366,6 +1374,7 @@ extern "C" napi_status node_api_create_syntax_error(napi_env env,
napi_value* result)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
NAPI_CHECK_VM_NOT_TERMINATING(env);
NAPI_CHECK_ENV_NOT_IN_GC(env);
return createErrorWithNapiValues(env, code, msg, JSC::ErrorType::SyntaxError, result);
}
@@ -1390,6 +1399,7 @@ extern "C" napi_status napi_create_type_error(napi_env env, napi_value code,
napi_value* result)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
NAPI_CHECK_VM_NOT_TERMINATING(env);
NAPI_CHECK_ENV_NOT_IN_GC(env);
return createErrorWithNapiValues(env, code, msg, JSC::ErrorType::TypeError, result);
}
@@ -1536,6 +1546,7 @@ extern "C" napi_status napi_create_error(napi_env env, napi_value code,
napi_value* result)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
NAPI_CHECK_VM_NOT_TERMINATING(env);
NAPI_CHECK_ENV_NOT_IN_GC(env);
return createErrorWithNapiValues(env, code, msg, JSC::ErrorType::Error, result);
}
@@ -1593,6 +1604,7 @@ extern "C" napi_status napi_create_range_error(napi_env env, napi_value code,
napi_value* result)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
NAPI_CHECK_VM_NOT_TERMINATING(env);
NAPI_CHECK_ENV_NOT_IN_GC(env);
return createErrorWithNapiValues(env, code, msg, JSC::ErrorType::RangeError, result);
}

View File

@@ -711,6 +711,12 @@ public:
void callFinalizer()
{
// Skip calling finalizers when VM is terminating (e.g., during process.exit)
// as they may try to create JavaScript objects/errors which is not allowed
if (env->isVMTerminating()) {
return;
}
// Calling the finalizer may delete `this`, so we have to do state changes on `this` before
// calling the finalizer
Bun::NapiFinalizer saved_finalizer = this->finalizer;