Joel Regus
Photo by Markus Spiske on Unsplash

How to Handle Syntax Errors in JavaScript

By Joel Regus on January 26, 2021 · 4 min read
Programming JavaScript

Is there a way to catch parsing errors?

The Backstory

I recently dealt with an interesting error in JavaScript. Take a look at the code below and see if you can find the error:

try {
  console.log("hello world");
} catch {
  console.log("an error occurred");
}

Did you see it?

This code threw the following error in older versions of Edge and Internet Explorer:


script1005 script1005 expected '('

Huh? What are they talking about?

Look closer:

// This:
} catch {

// Should be this:
} catch(e) {

According Microsoft's docs this is the cause of the error:

You attempted to enclose an expression within a set of parentheses, but did not include the opening parenthesis. Some expressions must be enclosed within a set of opening and closing parentheses.

Let's look at my example again:

try {
} catch(e) {
}

The e is called the CatchParameter, it is bound to the exception that occurs within a try block.

This error does not appear in other browsers. According to the spec the CatchParameter is optional:

Catch[Yield, Await, Return] :
	catch ( CatchParameter[?Yield, ?Await] ) Block[?Yield, ?Await, ?Return]
	catch Block[?Yield, ?Await, ?Return]

However, Edge and Internet Explorer seem to consider it to be mandatory.

The Problem

The error I dealt with occurred from within code that was loaded via a script element. Because this was a parse time error and not a run time error there was nothing I could do to prevent the entire program from crashing.

Wrapping the code inside the script element within a global try...catch does not work in this scenario because the code never runs.

So what can we do to catch these kind of errors?

The Solution

At first I thought of using the onerror attribute of the script element, but that only works for network errors.

Since syntax errors prevent the code from within the script element from running you will need to catch the error from the script element's window.

Check out this quote from MDN:

When a JavaScript runtime error (including syntax errors and exceptions thrown within handlers) occurs, an error event using interface ErrorEvent is fired at window and window.onerror() is invoked (as well as handlers attached by window.addEventListener (not only capturing)).

In other words you will need to add an error event listener to the window.

window.addEventListener("error", function(e){});

The problem with this approach is that it is very noisy. Every error that is thrown against the window will trigger our event handler. This is extra annoying within environments were a lot of third party code is running.

We can try to filter the errors by attempting to confirm that the error came from our script element.

I can think of a few ways of doing this:

document.currentScript

We can access which script is currently running by checking document.currentScript, here is what MDN has to say:

The Document.currentScript property returns the <script> element whose script is currently being processed and isn't a JavaScript module. (For modules use import.meta instead.)

It's important to note that this will not reference the <script> element if the code in the script is being called as a callback or event handler; it will only reference the element while it's initially being processed.

If we get a reference to our script tag we can compare it against the currentScript.

window.addEventListener("error", function(e){
  var script = document.getElementById("our_script");
  if (script && script === document.currentScript) {
    // Do something useful here
  }
});

The problem with this approach is that document.currentScript is not available in Internet Explorer, so make sure you do feature detection before you attempt to use it.

Error.prototype.fileName

The error object contains a property called fileName, this is what MDN has to say about it:

The fileName property contains the path to the file that raised this error.

We could use this to compare the url of our script against the script that threw the error.

window.addEventListener("error", function(e){
  var script = document.getElementById("our_script");
  if (script && script.src === e.fileName) {
    // Do something useful here
  }
});

This sounds nice, but according to MDN this feature is non-standard and should not be used in production. In my own testing I noticed that Chrome spelled this property as filename whereas MDN has it as fileName.

Error.prototype.stack

Perhaps we could look at the stack trace for clues? Lets check what MDN has to say:

The non-standard stack property of Error objects offer a trace of which functions were called, in what order, from which line and file, and with what arguments. The stack string proceeds from the most recent calls to earlier ones, leading back to the original global scope call.

We could use a regular expression to parse the stack and look for our script's file name.

I'll leave this as an exercise for the reader to determine how we could code this.

MDN says this feature is also non-standard and should not be used in production.

Conclusion

This was an interesting problem to think about, but I am not satisfied with these solutions. The broken code still dies, but at least now you can use your error monitoring tools to alert you of the issue. If you know of a better of handling this please get in touch!

Want to contact me?

Feel free to email me if you would like to get in touch.