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.
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.0node prices.js, nothing removed.- 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. - Then the name and the message.
TypeErroris the name, and everything after the colon is the message. Together they say what could not be done: something wasundefined, andtoFixedwas read from it. - 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.
- 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 line | What 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 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.
| Name | What it means | A typical cause |
|---|---|---|
SyntaxError | The text could not be read as JavaScript at all. | A missing bracket. Nothing in the file ran. |
ReferenceError | A 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. |
TypeError | The 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. |
RangeError | A number or a size outside what is allowed. | new Array(-1), or a function that calls itself forever. |
Error | The plain one, with no particular meaning. | Something a program raised deliberately, with a message of its own choosing. |
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')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
- Read the name. It tells you which of five kinds of problem this is.
- Read the message. It names the value or the operation that failed.
- Find the topmost
atline that names a file you wrote, and open that line. - Print the values that line uses, one
console.logper value, immediately above it. - One of them will be
undefinedornullor 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.