Skip to content
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

No stack trace with missing async keyword and dynamic imports #32177

Open
sbrl opened this issue Mar 10, 2020 · 10 comments
Open

No stack trace with missing async keyword and dynamic imports #32177

sbrl opened this issue Mar 10, 2020 · 10 comments
Labels
esm Issues and PRs related to the ECMAScript Modules implementation.

Comments

@sbrl
Copy link

sbrl commented Mar 10, 2020

What steps will reproduce the bug?

Consider the following code:

a.mjs

(async () => {
    "use strict";
    
	let b = await import("./b.mjs");
	await b.default();
})();

b.mjs

import fs from 'fs';

function bug() {
	// The something doesn't have to exist
	console.log(await fs.promises.readFile("/proc/cpuinfo", "utf-8"));
}
export default async function () {
	await bug();
}

Explanation

There is obviously a bug in b.mjs above. The bug() function is missing the async keyword`. However, when I attempt to execute the above, I get the following error:

(node:30870) UnhandledPromiseRejectionWarning: SyntaxError: Unexpected reserved word
    at Loader.moduleStrategy (internal/modules/esm/translators.js:81:18)
    at async link (internal/modules/esm/module_job.js:37:21)
(node:30870) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:30870) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

This isn't particularly helpful. Now, consider the following snippet:

c.mjs:

import fs from 'fs';

function bug() {
	// The something doesn't have to exist
	console.log(await fs.promises.readFile("/proc/cpuinfo", "utf-8"));
}

(async () => {
    "use strict";
    
    await bug();
})();

There's bug in this one too, but executing it yields a very different and much more helpful error:

file:///tmp/c.mjs:5
	console.log(await fs.promises.readFile("/proc/cpuinfo", "utf-8"));
	            ^^^^^

SyntaxError: Unexpected reserved word
    at Loader.moduleStrategy (internal/modules/esm/translators.js:81:18)
    at async link (internal/modules/esm/module_job.js:37:21)

Much better. Node gives us a helping hand by telling us where the error occurred.

How often does it reproduce? Is there a required condition?

This bug in the error message only appears to occur when a module is dynamically imported with import("./path/to/file.mjs"). Specifically, the first error message is missing this bit:

file:///tmp/c.mjs:5
	console.log(await fs.promises.readFile("/proc/cpuinfo", "utf-8"));
	            ^^^^^

...obviously the filepath and line number would be different for b.mjs is this bit was added.

What is the expected behavior?

The first error message should look like the second.

When dynamically importing a module that is missing the async keyword on a method, the error message should tell me where the error occurred.

What do you see instead?

The bit there it tells me where the "unexpected reserved word" was found is missing. See above for examples of what's gone wrong.

Additional information

If possible, when it's the await keyword that was detected as unexpected, the error message should reflect this more closely. For example, it might be helpful to say `SyntaxError: Unexpected reserved word "await" (did you forget the "async" keyword?)" or something like that.

@MylesBorins MylesBorins added the esm Issues and PRs related to the ECMAScript Modules implementation. label Mar 10, 2020
@MylesBorins
Copy link
Contributor

/cc @nodejs/modules

@coreyfarrell
Copy link
Member

See also #49441

@devsnek
Copy link
Member

devsnek commented Mar 10, 2020

this is a very general issue with how our error stack decoration works, and not specific to the esm loader. We can fix this in two different ways:

  • Patch ModuleWrap constructor to call stack decoration util
  • Move stack decoration to our prepareStackTrace handler (make all errors decorated)

@sbrl
Copy link
Author

sbrl commented Mar 10, 2020

Ah, I see @devsnek. Thanks for the explanation! I decided to report this issue because with a larger codebase it's actually a pretty significant time waster. For my PhD I have a dynamic import system for a number of subcommands that do different things in a Node.js application. It's becoming a serious challenge to figure out where I've made a mistake.....

I can grant private access to this codebase if this would help fixing the bug.

@NyanHelsing
Copy link

@sbrl Just want to note that adding a linter may help in this case by allowing you to locate errors and get that more useful stack trace without needing to actually execute the code.

@sbrl
Copy link
Author

sbrl commented Oct 1, 2020

@ExProbitasFiducia Right. I'm actually already using a linter, but it doesn't detect issues with async / await for some reason. I'm using Atom.

@NyanHelsing
Copy link

Shouldn't really matter which editor you have, the linter should perform the same even if you execute it from the cli.

I'm a fan of eslint and they do have a rule for async/await - https://eslint.org/docs/rules/require-await

Im not sure if this is useful, or related for you but I thought it was some neat info; there is a class of bugs associated with writing an async function that never uses the await keyword. I had an issue once where the await was in a conditional and I remember that was a pretty tricky thing to track down.

@ljharb
Copy link
Member

ljharb commented Oct 1, 2020

require-await is a terrible rule; it's perfectly valid to have an async function without using await. The issue is usually caused by forgetting to await a promise, which that rule won't actually help you with if you already have an await statement in your async function.

@NyanHelsing
Copy link

That's the way I understand async is supposed to work. As I mentioned I've found that there are circumstances in which an async function doesn't await a value causes problem. I hope you don't run into it but that rule prevents it from happening anyway...

@ljharb
Copy link
Member

ljharb commented Oct 1, 2020

Again, the issue there is that you're not awaiting a promise, which is unrelated to the presence or absence of await statements in a function.

gamemaker1 added a commit to express-rate-limit/express-rate-limit that referenced this issue Dec 28, 2021
- it gives this weird error:

  SyntaxError: Unexpected reserved word
    at Loader.moduleStrategy (internal/modules/esm/translators.js:140:18)
    at async link (internal/modules/esm/module_job.js:42:21)

- found these issues; should debug further:
	- nodejs/node#32177
	- https://github.com/nodejs/modules/issues/471
gamemaker1 added a commit to express-rate-limit/express-rate-limit that referenced this issue Dec 30, 2021
- it gives this weird error:

  SyntaxError: Unexpected reserved word
    at Loader.moduleStrategy (internal/modules/esm/translators.js:140:18)
    at async link (internal/modules/esm/module_job.js:42:21)

- found these issues; should debug further:
	- nodejs/node#32177
	- https://github.com/nodejs/modules/issues/471
gamemaker1 added a commit to express-rate-limit/express-rate-limit that referenced this issue Dec 30, 2021
- it gives this weird error:

  SyntaxError: Unexpected reserved word
    at Loader.moduleStrategy (internal/modules/esm/translators.js:140:18)
    at async link (internal/modules/esm/module_job.js:42:21)

- found these issues; should debug further:
	- nodejs/node#32177
	- https://github.com/nodejs/modules/issues/471
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
esm Issues and PRs related to the ECMAScript Modules implementation.
Projects
None yet
Development

No branches or pull requests

6 participants