Debugging Syntax Error Unexpected Reserved Word in Mocha on Modern Node.js

; Date: Sun Aug 07 2022

Tags: Node.JS »»»» JavaScript

The error Unexpected Reserved Word usually means you're using await in a non-async function. But what if Mocha throws this error, citing a test suite file that's clean of such problems? To save spending fruitless hours, read this short article.

The error Unexpected Reserved Word means that the JavaScript compiler saw a reserved word, meaning a keyword special to the compiler, when it wasn't expected. THe typical case is using await in a function that hasn't been marked async.

In this case I found Mocha - a popular framework for writing unit tests - to incorrectly report which source file contained the unexpected reserved word. My test suite threw that error message, but Mocha identified the incorrect source file.

Usually a compiler, or a test framework, reporting a syntax error will tell us the actual line of code, and the offending code. But, in this case Mocha does not report the line number, nor the affected code, and to make it worse it reports the error as occuring in a different source file from its actual location.

Because Mocha incorrectly reported the location of the unexpected reserved word error, I spent fruitless hours double and triple checking the syntax of a source file which contained no such error.

Let's start with the usual case:

$ cat unreserved.mjs 

function a() {
    await console.log(`This will throw an error`);
}

$ node unreserved.mjs 
file:///home/david/Projects/akasharender/akasharender/test/unreserved.mjs:3
    await console.log(`This will throw an error`);
    ^^^^^

SyntaxError: Unexpected reserved word
    at ESMLoader.moduleStrategy (node:internal/modules/esm/translators:119:18)
    at ESMLoader.moduleProvider (node:internal/modules/esm/loader:483:14)
    at async link (node:internal/modules/esm/module_job:67:21)

This is what we normally see. We've created an ES6 module, unreserved.mjs, containing a non-async function. Within that function is the await keyword. That's a use of a reserved word, await, in the incorrect context. In this case the error report does an excellent job of letting us know what the problem is, and where it occurred.

This is the desired behavior, for the compiler to do a good job of letting us know what's going on. We can go directly to the affected line of source code, and immediately know what to do, after groaning "Doh".

Consider a real package, where an actual (and useful) module has been written, which contains that problem. Then, you go to write a test suite for this module, because you're following good test-first-development practices.

$ cat test-unreserved.mjs 

import * as unreserved from './unreserved.mjs';

$ npx mocha test-unreserved.mjs 

SyntaxError[ @/home/david/Projects/akasharender/akasharender/test/test-unreserved.mjs ]: Unexpected reserved word
    at ESMLoader.moduleStrategy (node:internal/modules/esm/translators:119:18)
    at ESMLoader.moduleProvider (node:internal/modules/esm/loader:483:14)

Your test suite of course must import the application code to execute test cases. This means the unreserved.mjs module. Because we just created that file, we can easily remember the error. In this case, Mocha doesn't give us a clean error message. It simply says Unexpected reserved word, and further it identifies test-unreserved.mjs as containing the problem.

Again, consider that this is instead happening in a real package. The test suite could have 1500 lines of code, and the module being tested has a similar amount of code.

Because Mocha positively identifies the test suite as containing the unexpected reserved word, it's easy to then spend several hours combing through the test suite. You check every use of await and it's all correct. You check closely every loop, every function, every test case. You use VS Code and collapse every block, and because the editor correctly collapses each block, it seems the editor believes this is correctly constructed JavaScript. You then try commenting out all code, and still the error shows up. Finally you try commenting out the import statements one by one, and voila the error goes away with one particular import statement.

Checking inside that file you might then find a non-async function where the async keyword was used. That's easily fixed, and that particular error goes away. There may have been other problems, and you'll know the original problem was fixed because the error message changes.

$ npx mocha test-cache.mjs 

SyntaxError[ @/home/david/Projects/akasharender/akasharender/test/test-cache.mjs ]: Unexpected reserved word
    at ESMLoader.moduleStrategy (node:internal/modules/esm/translators:119:18)
    at ESMLoader.moduleProvider (node:internal/modules/esm/loader:483:14)

$ npx mocha test-cache.mjs 

SyntaxError[ @/home/david/Projects/akasharender/akasharender/test/test-cache.mjs ]: Identifier 'setup' has already been declared
    at ESMLoader.moduleStrategy (node:internal/modules/esm/translators:119:18)
    at ESMLoader.moduleProvider (node:internal/modules/esm/loader:483:14)

This is from the real instance where the problem occurred. I had fixed the unexpected reserved word issue, only to learn of another problem -- that I'd created two functions named setup. Notice that Mocha still reported the wrong source file. But, its clear the problem will be in the other source file and not the test suite.

Conclusion

Mocha is incorrectly reporting the source file containing the code problems.

It correctly identified the problem - that some code used a reserved word incorrectly. But the error was reported in the wrong file, which can easily lead to fruitless hours carefully scrutinizing code that has no such errors.

Our time is valuable, and it's frustrating to spend so much time cleaning up a file that had no problems in it.

Bad Mocha. No soup for you.

About the Author(s)

(davidherron.com) David Herron : David Herron is a writer and software engineer focusing on the wise use of technology. He is especially interested in clean energy technologies like solar power, wind power, and electric cars. David worked for nearly 30 years in Silicon Valley on software ranging from electronic mail systems, to video streaming, to the Java programming language, and has published several books on Node.js programming and electric vehicles.

Books by David Herron

(Sponsored)