Master JavaScript

What JavaScript is, and how to run it

4 / 266
Contents

Contents

Master JavaScript

0 of 266 complete

11 min read

Reading an error all the way down

The name, the message and the stack: what each part of a failure tells you, and which line of your own file to open.

A program that stops with an error has told you more about itself than a program that quietly works. The first line names what could not be done, and the lines below it name the exact place it happened. Most people read the first line, guess at the rest, and throw away the useful half.

Here is a small program with a real mistake in it. Run it and read what comes back before going on.

Try CodeJavaScript

It stops with TypeError: Cannot read properties of undefined (reading 'toFixed'). The bundle handed to lineTotal has a name and no price. Add price: 4 inside the braces, after a comma, and run it again.

The same file run by Node.js in a terminal gives the whole report rather than a summary. This is all of it, and every part of it is worth a sentence.

$ node prices.js
/home/you/shop/prices.js:2
  return price.toFixed(2);
               ^

TypeError: Cannot read properties of undefined (reading 'toFixed')
    at priceWithVat (/home/you/shop/prices.js:2:16)
    at lineTotal (/home/you/shop/prices.js:6:10)
    at Object.<anonymous> (/home/you/shop/prices.js:9:13)
    at Module._compile (node:internal/modules/cjs/loader:1854:14)
    at Object..js (node:internal/modules/cjs/loader:1985:10)
    at Module.load (node:internal/modules/cjs/loader:1577:32)
    at Module._load (node:internal/modules/cjs/loader:1379:12)
    at wrapModuleLoad (node:internal/modules/cjs/loader:255:19)
    at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5)
    at node:internal/main/run_main_module:33:47

Node.js v24.17.0
The complete output of node prices.js, nothing removed.
  1. The first three lines are a picture of the scene. The file and line number, the source line itself, and a ^ under the exact character. Node.js prints this only for an error that stopped the program.
  2. Then the name and the message. TypeError is the name, and everything after the colon is the message. Together they say what could not be done: something was undefined, and toFixed was read from it.
  3. Then the lines beginning with `at`. This is the stack: the list of calls that were in progress at that moment, most recent first. It says how the program arrived at the broken line.
  4. Then the version of Node.js. Useful when you are reporting a problem to somebody else, and never otherwise.

The stack, and which line to open

When one function calls another, the first one is not finished; it is waiting. The call stack is the list of everything currently waiting, and an error prints it so you can see the route that was taken rather than only the destination.

The lineWhat it is telling you
at priceWithVat (.../prices.js:2:16)The call that was running when it broke: line 2, character 16 of your file.
at lineTotal (.../prices.js:6:10)The call that called it, from line 6.
at Object.<anonymous> (.../prices.js:9:13)The top level of your file, line 9. <anonymous> means code that is not inside any named function.
at Module._compile (node:internal/...)Node.js loading your file in the first place. Not your code, and never the place to look.
The stack above, line by line. Each entry is a name, then a file, a line number and a character number.

The rule that falls out of that: read from the top, and stop at the first line naming a file you wrote. That is where to look, and everything below it is how you got there.

Two kinds of line are worth learning to skip. Anything whose path starts with node: is Node.js's own machinery. Anything whose path contains node_modules is inside somebody else's library, and while it is occasionally their bug, the honest first guess is that your code handed them something they did not expect.

The name in front of the message

There are a handful of names, and each one narrows the problem down before you have read a word of the message.

NameWhat it meansA typical cause
SyntaxErrorThe text could not be read as JavaScript at all.A missing bracket. Nothing in the file ran.
ReferenceErrorA name was used that does not exist, or is not ready yet.A typo in a variable name, or reading a let above its own line.
TypeErrorThe value was there and it was the wrong kind of thing.Calling something that is not a function, or reading a property of undefined or null.
RangeErrorA number or a size outside what is allowed.new Array(-1), or a function that calls itself forever.
ErrorThe plain one, with no particular meaning.Something a program raised deliberately, with a message of its own choosing.
The five you will meet in this course.

TypeError is the one you will see most, and its two commonest messages are worth recognising on sight. "Cannot read properties of undefined" means you reached into something that was not there. "x is not a function" means you put brackets after something that was not one.

Did any of it run?

This is the distinction that saves the most time, and the output tells you which you are looking at without any effort on your part.

$ cat broken.js
console.log("line one ran");
const total = (2 + 3;
console.log(total);

$ node broken.js
/home/you/shop/broken.js:2
const total = (2 + 3;
                    ^

SyntaxError: Unexpected token ';'

$ cat half.js
console.log("line one ran");
const names = null;
console.log(names.length);

$ node half.js
line one ran
/home/you/shop/half.js:3
console.log(names.length);
                  ^

TypeError: Cannot read properties of null (reading 'length')
Two files with the same first line. Only one of them prints it. The at lines are cut from both, to keep the two failures side by side.

A SyntaxError is raised while the file is being read, before a single statement is carried out. Nothing runs, not even a console.log on line one, which is why broken.js printed nothing before failing.

Every other error happens partway through a run. Everything printed before it really did happen, so the last thing you see printed is a fact about how far the program got.

So if you were expecting output and there is none at all, suspect the shape of the file rather than its logic. If some of the output arrived and then it stopped, the last line printed tells you where to start reading.

The routine

  1. Read the name. It tells you which of five kinds of problem this is.
  2. Read the message. It names the value or the operation that failed.
  3. Find the topmost at line that names a file you wrote, and open that line.
  4. Print the values that line uses, one console.log per value, immediately above it.
  5. One of them will be undefined or null or the wrong kind of thing. Now work backwards to where that value came from.

Step four is the one people skip, and it is the one that ends the guessing. The line that failed is rarely the line that is wrong; it is the line where a wrong value finally met an operation that could not accept it.

An error's stack begins with four at lines inside node_modules/express, and the fifth names /home/you/app/routes.js:41:22. Which line do you open first, and why?

Line 41 of your own routes.js, at character 22. The four lines above it are inside a library, and the value that broke it almost certainly came from the call you made on line 41. Library code is run by thousands of people every day, so the first guess is that your code handed it something unexpected, not that the library is broken. Read down the stack until you meet a file you wrote and start there.

Check yourself

A file has console.log("starting"); on line 1 and a missing closing bracket on line 12. What appears when you run it?

All lessons in Master JavaScript