Tom MacWright

tom@macwright.com

Errors

An understanding of errors is absolutely vital to the task of coding. Absolutely everyone, including a first-day beginner to a PhD at NASA, makes mistakes constantly. You can get better, graduate from one kind of error to another, but making things will always be a sort of trial-and-error.

The lack of focus on understanding errors is disheartening, even for self-education sites like Codecademy, whose JavaScript course goes to great lengths to avoid teaching error literacy.

Not understanding errors means that your programs “work” or “don’t work”, and in-between is a feeling of helplessness and hours spent guessing. This is a waste. There’s information available that says what sorts of problems exist and where they come from - all you need to do is understand it.

Diagnosing errors is also a vital skill for using other people’s code: it’ll help you report better bugs and point to the cause faster.

Compile vs. Runtime Errors

There are two main types of errors: compile errors and runtime errors.1

Compile-time errors occur before any part of your code runs. For instance, if you write something that the JavaScript interpreter simply can’t understand at any point in your program, like if you forget a ", then it will simply give up on running any part of the program and report its confusion.

Runtime errors, on the other hand, happen sometime while your code is running. This could be almost immediately, like in the case of a ReferenceError, or it could happen 5 minutes after someone has been using a website, when some bad data mucks everything up.

All right: let’s meet the errors.

SyntaxError

codeoutput
console.log('running');
@
syntax.js:2
@
^
SyntaxError: Unexpected token ILLEGAL

SyntaxError is potentially the most commonly seen error for newcomers. Some common cases in which you’ll see it crop up are:

  • You’ve typed an opening (, {, [, or ', but not the closing ), }, ], '
  • Your code includes an illegal character like @, #, <, or > that isn’t in a string. Identifiers in JavaScript - names of functions and variables - can have lots of names, but not all.
  • You’ve tried to name a variable or function the same thing as a reserved word in JavaScript, like break or this.

As the example above demonstrates, SyntaxErrors are often flagged by the syntax highlighting in your editor: if you type var break = 1; in Sublime, you’ll notice that break is red, the color of a keyword, rather than the color of a variable.

SyntaxErrors are usually compile-time errors: even though this example has the code console.log('running') above the error, that never runs, and you never see running in your console. The SyntaxError shuts it all down before a single line of code is run.

There are two big cases where SyntaxErrors are not compile-time:

  • Parsing invalid JSON with JSON.parse
  • Evaluating JavaScript with eval

Both of these errors can happen at any time: you use AJAX to get some data and it’s invalid, and you’ll get a SyntaxError.

ReferenceError

codeoutput
console.log('running');
var b = a;
running

reference.js
var b = a
^
ReferenceError: a is not defined

ReferenceError means “the variable you referenced does not exist”. A few popular common ways this crops up are:

  • Misspelled words. Identifiers in JavaScript are case-sensitive and, unlike the wonderful human brain, computers are not forgiving of single-letter typing mistakes.
  • Trying to use a variable that is out of scope or hasn’t been defined yet.

TypeError

codeoutput
console.log('running');
(0).toUpperCase();
running

type.js:2
(0).toUpperCase();
    ^
TypeError: Object 0 has no method 'toUpperCase'

While JavaScript is very flexible when it comes to types - you can, for instance, add a number to a string without incident - not all of its types support all methods or operations. The TypeError crops up when you accidentally mistake one kind of variable for another. This crops up when:

  • You give the wrong arguments to an existing function and it tries to call a method that’s only defined on one type (like in this case, toUpperCase) on the type you gave it.

There’s also another case where you might run into errors if you roll with the no-semicolon crowd:

codeoutput
console.log('running');
var x = 10
var y = x
(function() { console.log('hi'); })()
running

type_asi.js:3
var y = x
        ^
TypeError: number is not a function

In this particular case, JavaScript sees your code and smooshes the line var y = x together with the immediately-invoked function below it, and sees

var y = x(function() { console.log('hi'); })()

x is a number, not a function, hence the error. The message is illustrative of another fact: the line & snippet given is where the error starts, not necessarily the full context of why it happened. In this case you need to read both lines to see the problem.

RangeError

a broken car

codeoutput
console.log('running');
new Array(-1);
running

range_error_array.js:2
new Array(-1);
    ^
RangeError: Invalid array length

RangeError is thrown when you give errors that are outside of the logical realm: for instance, here I’m trying to create an Array with length = -1. Like the physical world, there is nothing less than emptiness.

But given that most of the time you should use the [] literal Array constructor and the other places this crops up, like Number.toPrecision, aren’t super used, this is the minority of places you’ll see it. Most of the time you’ll see this case:

codeoutput
console.log('running');
(function infinite() {
  infinite();
})();
running

range_error.js:2
(function infinite() {
                  ^
RangeError: Maximum call stack size exceeded

RangeError is used for the error you get when you write an infinitely recursive function and just let it run. This is the equivalent of an infinite loop, but given JavaScript’s lack of tail calls, every time you recurse you add a little bit more overhead of remembering a new function call. So, V8 (Chrome & Node) will alert you with this Maximum call stack size exceeded message telling you that your recursion is out of control.

URIError

For the sake of compleness, we’ll mention URIError, but it’s really not very common.

codeoutput
console.log('running');
decodeURIComponent('%');
running

uri_error.js:2
decodeURIComponent('%');
^
URIError: URI malformed

JavaScript includes several functions that encode and decode URIs (most of which are also URLs), like decodeURI(), decodeURIComponent(), encodeURI(), and encodeURIComponent(). Not every URI is valid - sometimes you’ll have a case like this one, where the percent character is only valid with two hexademical digits after it to serve as percent encoding. And if you give one of these invalid inputs to the URI-eating functions, they’ll reject it roundly with this error.

Until Next Time

Knowing the basic types of errors should get you started on the path to seeing those cryptic messages as helpful hints. In the next episode, I’ll discuss the most important parts of Google Chrome that you don’t use yet, the call stack, breakpoints, and more. Feel free to hit me up at @tmcw with questions or comments.

I’ve compiled examples of each error type in a GitHub repository: clone it and run them in node, or copy them and run them in your developer console to try them out.

Notes

  1. Sticklers will note that JavaScript is not (traditionally) a compiled language. The line between compiled & interpreted languages has been significantly blurred in the last 10 years, and this nomenclature makes more sense than calling them simply ‘Syntax errors’ because features in Harmony will make non-syntax errors throw before evaluation happens.