-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
async_hooks: async hook stack assertion shadows "Maximum call stack size exceeded" #15448
Comments
/cc @nodejs/async_hooks I can reproduce this with |
I think is mostly a stack overflow issue; 6: const FooBar = async function fetch(url, encoding) {
7:
8: const res = fetch(url); // Programmer forgot `await` here! But I agree the error should be |
Standalone reproduction: async function fn() {
fn();
throw new Error();
}
(async function() { await fn(); })() Edit: Without the |
#15454 skips the sanity check when |
Thank you. Looking into it. |
Thanks for reporting this. Quick update, this has to do w/ Promises and
whereas: require('async_hooks').createHook({ init() { } }).enable();
async function fn() {
fn();
throw new Error();
}
(async function() { await fn(); })() gives:
|
There also seems to be a discrepancy depending on when this runs: process.nextTick(() => {
async function fn() {
fn();
throw new Error();
}
(async function() { await fn(); })()
}); produces
over 4,000 times, and has an exit code of EDIT: nix that. Simply changing the script to async function fn() {
fn();
throw new Error();
}
(async function() { await fn(); })()
process._rawDebug('------ AFTER -----'); outputs the same as above. |
Have a fix at #15553. Thanks for the bug report. |
If bootstrap throws and if ids are added to the async id stack and if the exception wasn't handled by the fatal exception handler then the AsyncCallbackScope destructor will cause the AsyncHooks::pop_ids() stack check to fail. Causing the application to crash. So clear the async id stack manually. This is only possible if the user: 1) manually calls MakeCallback() or 2) uses async await in the top level. Which will cause _tickCallback() to fire before bootstrap finishes executing. The following example shows how the application can fail due to exceeding the maximum call stack while using async await: async function fn() { fn(); throw new Error(); } (async function() { await fn(); })(); If this occurs during bootstrap then the application will pring the following warning a number of times then exit with a status of 0: (node:*) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: *): Error Here's the same recursive call done after enabling a new AsyncHook() the following will print instead of the above warning and exit with a non-zero code (currently it's 7 because of how node::FatalException assigns error codes based on where the failure happened): script.js:25 async function fn() { ^ RangeError: Maximum call stack size exceeded at <anonymous> at fn (script.js:25:18) at fn (script.js:26:3) .... This has to do with how Promises lazily enable PromiseHook if an AsyncHook() is enabled. Whether these need to be made uniform is outside the scope of this commit Fixes: nodejs#15448 PR-URL: nodejs#15553 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
If bootstrap throws and if ids are added to the async id stack and if the exception wasn't handled by the fatal exception handler then the AsyncCallbackScope destructor will cause the AsyncHooks::pop_ids() stack check to fail. Causing the application to crash. So clear the async id stack manually. This is only possible if the user: 1) manually calls MakeCallback() or 2) uses async await in the top level. Which will cause _tickCallback() to fire before bootstrap finishes executing. The following example shows how the application can fail due to exceeding the maximum call stack while using async await: async function fn() { fn(); throw new Error(); } (async function() { await fn(); })(); If this occurs during bootstrap then the application will pring the following warning a number of times then exit with a status of 0: (node:*) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: *): Error Here's the same recursive call done after enabling a new AsyncHook() the following will print instead of the above warning and exit with a non-zero code (currently it's 7 because of how node::FatalException assigns error codes based on where the failure happened): script.js:25 async function fn() { ^ RangeError: Maximum call stack size exceeded at <anonymous> at fn (script.js:25:18) at fn (script.js:26:3) .... This has to do with how Promises lazily enable PromiseHook if an AsyncHook() is enabled. Whether these need to be made uniform is outside the scope of this commit Fixes: #15448 PR-URL: #15553 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
If bootstrap throws and if ids are added to the async id stack and if the exception wasn't handled by the fatal exception handler then the AsyncCallbackScope destructor will cause the AsyncHooks::pop_ids() stack check to fail. Causing the application to crash. So clear the async id stack manually. This is only possible if the user: 1) manually calls MakeCallback() or 2) uses async await in the top level. Which will cause _tickCallback() to fire before bootstrap finishes executing. The following example shows how the application can fail due to exceeding the maximum call stack while using async await: async function fn() { fn(); throw new Error(); } (async function() { await fn(); })(); If this occurs during bootstrap then the application will pring the following warning a number of times then exit with a status of 0: (node:*) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: *): Error Here's the same recursive call done after enabling a new AsyncHook() the following will print instead of the above warning and exit with a non-zero code (currently it's 7 because of how node::FatalException assigns error codes based on where the failure happened): script.js:25 async function fn() { ^ RangeError: Maximum call stack size exceeded at <anonymous> at fn (script.js:25:18) at fn (script.js:26:3) .... This has to do with how Promises lazily enable PromiseHook if an AsyncHook() is enabled. Whether these need to be made uniform is outside the scope of this commit Fixes: #15448 PR-URL: #15553 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
If bootstrap throws and if ids are added to the async id stack and if the exception wasn't handled by the fatal exception handler then the AsyncCallbackScope destructor will cause the AsyncHooks::pop_ids() stack check to fail. Causing the application to crash. So clear the async id stack manually. This is only possible if the user: 1) manually calls MakeCallback() or 2) uses async await in the top level. Which will cause _tickCallback() to fire before bootstrap finishes executing. The following example shows how the application can fail due to exceeding the maximum call stack while using async await: async function fn() { fn(); throw new Error(); } (async function() { await fn(); })(); If this occurs during bootstrap then the application will pring the following warning a number of times then exit with a status of 0: (node:*) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: *): Error Here's the same recursive call done after enabling a new AsyncHook() the following will print instead of the above warning and exit with a non-zero code (currently it's 7 because of how node::FatalException assigns error codes based on where the failure happened): script.js:25 async function fn() { ^ RangeError: Maximum call stack size exceeded at <anonymous> at fn (script.js:25:18) at fn (script.js:26:3) .... This has to do with how Promises lazily enable PromiseHook if an AsyncHook() is enabled. Whether these need to be made uniform is outside the scope of this commit Fixes: nodejs/node#15448 PR-URL: nodejs/node#15553 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
If bootstrap throws and if ids are added to the async id stack and if the exception wasn't handled by the fatal exception handler then the AsyncCallbackScope destructor will cause the AsyncHooks::pop_ids() stack check to fail. Causing the application to crash. So clear the async id stack manually. This is only possible if the user: 1) manually calls MakeCallback() or 2) uses async await in the top level. Which will cause _tickCallback() to fire before bootstrap finishes executing. The following example shows how the application can fail due to exceeding the maximum call stack while using async await: async function fn() { fn(); throw new Error(); } (async function() { await fn(); })(); If this occurs during bootstrap then the application will pring the following warning a number of times then exit with a status of 0: (node:*) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: *): Error Here's the same recursive call done after enabling a new AsyncHook() the following will print instead of the above warning and exit with a non-zero code (currently it's 7 because of how node::FatalException assigns error codes based on where the failure happened): script.js:25 async function fn() { ^ RangeError: Maximum call stack size exceeded at <anonymous> at fn (script.js:25:18) at fn (script.js:26:3) .... This has to do with how Promises lazily enable PromiseHook if an AsyncHook() is enabled. Whether these need to be made uniform is outside the scope of this commit Fixes: nodejs/node#15448 PR-URL: nodejs/node#15553 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
If bootstrap throws and if ids are added to the async id stack and if the exception wasn't handled by the fatal exception handler then the AsyncCallbackScope destructor will cause the AsyncHooks::pop_ids() stack check to fail. Causing the application to crash. So clear the async id stack manually. This is only possible if the user: 1) manually calls MakeCallback() or 2) uses async await in the top level. Which will cause _tickCallback() to fire before bootstrap finishes executing. The following example shows how the application can fail due to exceeding the maximum call stack while using async await: async function fn() { fn(); throw new Error(); } (async function() { await fn(); })(); If this occurs during bootstrap then the application will pring the following warning a number of times then exit with a status of 0: (node:*) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: *): Error Here's the same recursive call done after enabling a new AsyncHook() the following will print instead of the above warning and exit with a non-zero code (currently it's 7 because of how node::FatalException assigns error codes based on where the failure happened): script.js:25 async function fn() { ^ RangeError: Maximum call stack size exceeded at <anonymous> at fn (script.js:25:18) at fn (script.js:26:3) .... This has to do with how Promises lazily enable PromiseHook if an AsyncHook() is enabled. Whether these need to be made uniform is outside the scope of this commit Fixes: #15448 PR-URL: #15553 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
PR-URL: #15454 Ref: #14387 Ref: #14722 Ref: #14717 Ref: #15448 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
PR-URL: nodejs/node#15454 Ref: nodejs/node#14387 Ref: nodejs/node#14722 Ref: nodejs/node#14717 Ref: nodejs/node#15448 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
PR-URL: nodejs/node#15454 Ref: nodejs/node#14387 Ref: nodejs/node#14722 Ref: nodejs/node#14717 Ref: nodejs/node#15448 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
v8.2.1
Linux 4.10.0-33-generic #37~16.04.1-Ubuntu SMP Fri Aug 11 14:07:24 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
My product is not affected by this bug, but I report it anyway because it's still a bug and may affect someone else.
Expected
User-friendly error message is expected, not this stack error.
The text was updated successfully, but these errors were encountered: