diff --git a/src/node.cc b/src/node.cc index 5b1c4c7748fbf6..9376204637e075 100644 --- a/src/node.cc +++ b/src/node.cc @@ -3727,7 +3727,16 @@ void LoadEnvironment(Environment* env) { // who do not like how bootstrap_node.js sets up the module system but do // like Node's I/O bindings may want to replace 'f' with their own function. Local arg = env->process_object(); - f->Call(Null(env->isolate()), 1, &arg); + auto ret = f->Call(env->context(), Null(env->isolate()), 1, &arg); + // If there was an error during bootstrap then it was either handled by the + // FatalException handler or it's unrecoverable (e.g. max call stack + // exceeded). Either way, clear the stack so that the AsyncCallbackScope + // destructor doesn't fail on the id check. + // There are only two ways to have a stack size > 1: 1) the user manually + // called MakeCallback or 2) user awaited during bootstrap, which triggered + // _tickCallback(). + if (ret.IsEmpty()) + env->async_hooks()->clear_async_id_stack(); } static void PrintHelp() { diff --git a/test/parallel/test-async-wrap-pop-id-during-load.js b/test/parallel/test-async-wrap-pop-id-during-load.js new file mode 100644 index 00000000000000..1017fc02a72b05 --- /dev/null +++ b/test/parallel/test-async-wrap-pop-id-during-load.js @@ -0,0 +1,21 @@ +'use strict'; + +require('../common'); + +if (process.argv[2] === 'async') { + async function fn() { + fn(); + throw new Error(); + } + (async function() { await fn(); })(); + // While the above should error, just in case it dosn't the script shouldn't + // fork itself indefinitely so return early. + return; +} + +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +const ret = spawnSync(process.execPath, [__filename, 'async']); +assert.strictEqual(ret.status, 0); +assert.ok(!/async.*hook/i.test(ret.stderr.toString('utf8', 0, 1024)));