Worked example
A stack trace, read from the program
checkout passes cart.items, but the cart has a typo: item. total then reads reduce from undefined. The program catches the error and prints the first four lines of its stack, with this file’s full URL replaced by main.js: the error, the frame where it happened, the caller, and the top level of the module.
main.js
function total(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
function checkout(cart) {
return total(cart.items);
}
try {
checkout({item: [{price: 3}]}); // a typo: item instead of items
} catch (error) {
// The first four lines of the stack, with this file shown as main.js:
const lines = error.stack.split("\n").slice(0, 4);
console.log(lines.join("\n").replaceAll(import.meta.url, "main.js"));
}
Output
TypeError: Cannot read properties of undefined (reading 'reduce')
at total (main.js:2:16)
at checkout (main.js:6:10)
at main.js:10:3
- Line 2, column 16 is the dot before reduce: the place where undefined was read.
- The bug is in the caller’s data, two frames further down: read on until you reach the line that passed the wrong value.
- error.stack is a plain string; its exact form is V8’s, so do not parse it in production code.
Common mistakes
Not reading the message
const names = ["ada", "linus"];
for (const name of names) {
console.log(name.toUppercase());
}
What Node.js prints
TypeError: name.toUppercase is not a function
Why, and the fix
The message already names the problem: name.toUppercase does not exist, so it is undefined, and undefined cannot be called. Look for a typo before you look for anything else: the method is toUpperCase, with a capital C.
A trace that repeats one function
function countDown(n) {
console.log(n);
return countDown(n - 1);
}
countDown(3);
What Node.js prints
RangeError: Maximum call stack size exceeded
Why, and the fix
The trace shows countDown again and again: the function calls itself with no end. Every recursive function needs a case that stops: if (n === 0) return;. A trace full of the same frame always points to that.
An error message without context
function setAge(user, text) {
const age = Number(text);
if (Number.isNaN(age)) throw new Error("Invalid value");
user.age = age;
}
setAge({name: "Ada"}, "forty");
What Node.js prints
Error: Invalid value
Why, and the fix
The trace shows where the error was thrown, but the message does not say which value was wrong or why. Put the facts into the message: throw new TypeError(`age must be a number, got "${text}"`). Whoever reads the log, maybe you in six months, can then fix it without a debugger.
JavaScript in the browser: your browser’s own engine, in a sandboxed worker. Syntax errors are located with acorn 8.18.0, MIT. Licence and source