How to Read an Error
Mental model: An error is a message with an address: the name says what kind, the stack says where.
Level: beginner · about 10 minutes
Errors are the most useful output your program produces. Every one has the same three parts, and once you can name them you stop reading errors as noise.
try {
const user = null;
console.log(user.name);
} catch (err) {
console.log(err.name); // → 'TypeError'
console.log(err.message); // → "Cannot read properties of null (reading 'name')"
console.log(err.stack.split('\n')[0]); // → the first line of the trace
}
name- the category:
TypeError,ReferenceError,SyntaxError,RangeError, orError message- the specific complaint, usually naming the value or property involved
stack- the call path, newest frame first, with file, line and column
Reading a stack trace
TypeError: Cannot read properties of undefined (reading 'total')
at formatCart (cart.js:42:18) ◄── where it blew up (YOUR CODE)
at renderPage (page.js:17:9) ◄── who called that
at handleClick (app.js:88:5) ◄── who called that
at HTMLButtonElement.onclick ◄── the host, not you
name ──► TypeError
message ──► Cannot read properties of undefined (reading 'total')
address ──► cart.js, line 42, column 18
- Read the name first It tells you the kind of mistake before you look at anything else.
ReferenceErrormeans a name problem.TypeErrormeans the value was not what you assumed. - Read the message literally It usually names the exact property or value. "reading
total" means the thing to the left of.totalwasundefined, so look one step earlier than the line you are on. - Find the first frame in your own code The top frame may be inside a library. Scan down to the first file you wrote. That is where your assumption broke.
- Print the thing that was wrong Do not guess. Log the value one line above the failure and confirm what it actually is.
- Fix the cause, not the symptom Adding
?.silences "cannot read properties of undefined" but leaves you with a missing cart. Ask why it was undefined.
The four names you will meet this week
| Name | Means | Classic trigger |
|---|---|---|
SyntaxError | the code could not be parsed, so nothing ran | a missing bracket or quote |
ReferenceError | that name does not exist here | a typo, a missing import, or the wrong scope |
TypeError | the value exists but is the wrong kind for what you did | calling a non-function, reading a property of undefined |
RangeError | a legal kind of value, an illegal amount of it | runaway recursion, new Array(-1) |
const cases = [
() => JSON.parse('{oops}'),
() => missingName,
() => undefined.length,
() => new Array(-1),
];
for (const attempt of cases) {
try {
attempt();
} catch (err) {
console.log(err.name);
}
}
// → SyntaxError, ReferenceError, TypeError, RangeErrorFour errors, four names. Note that JSON.parse throws a SyntaxError at runtime.
Message to cause, the short version
| The message says | What actually happened | Where to look |
|---|---|---|
x is not defined | no binding with that name is in scope | spelling, a missing import, or a declaration inside another block |
Cannot read properties of undefined (reading 'y') | the value before .y was undefined | the expression one step to the left, or the function that was meant to return it |
x is not a function | the name exists but holds something else | a typo in the method name, or a value you forgot to call or return |
Cannot access 'x' before initialization | a let or const was used above its declaration | move the declaration up; this is the temporal dead zone |
Assignment to constant variable | you reassigned a const | use let, or mutate the object instead of replacing it |
Unexpected token } | brackets or quotes do not balance | the lines above the reported one |
Maximum call stack size exceeded | a function called itself with no exit | the base case of your recursion |
Converting circular structure to JSON | the object references itself | the property that points back to a parent |
Invalid array length | a negative or fractional length | the number you passed to new Array or length |
console.log('one');
undefinedFunction();
console.log('two');This is a runtime error, not a parse error, so line 1 runs and logs one. Then the missing name throws a ReferenceError and the script stops, so two never appears. Compare that with a SyntaxError, where you would see no output at all: what you did or did not see printed is itself a diagnostic.
Try it yourself
The error zoo
function tryIt(label, fn) {
try {
fn();
console.log(label, '→ no error');
} catch (err) {
console.log(label, '→', err.name + ':', err.message);
}
}
tryIt('parse bad JSON ', () => JSON.parse('{oops}'));
tryIt('use a missing name', () => nothingHere);
tryIt('read from undefined', () => undefined.length);
tryIt('bad array length ', () => new Array(-1));
tryIt('perfectly fine ', () => [1, 2].map((n) => n * 2));
Add a fifth case that throws a RangeError by recursing forever. Then throw your own error with throw new Error('with a useful message').
Exercises
Classify the failure
Write classify(fn) which calls fn and returns the name of the error it threw, as a string. If fn runs without throwing, return 'ok'. Do not let any error escape.
Throw an error worth reading
Write parseAge(input) which accepts a string of digits and returns it as a number. Throw a TypeError if input is not a string, or is a string that is not entirely digits. Throw a RangeError if the number is above 130. Every message must include the value you rejected, so the person reading the stack trace does not have to guess.
Check yourself
- What does this log?
- undefined — Reading a property that does not exist gives
undefinedrather than an error, so the typo inlenghtis silent. This is why the error you eventually see is often several lines away from the mistake: theundefinedtravels before it breaks something. useris{}. What doesuser.save()throw?TypeError: user.save is not a function—useris found, so it is not a reference problem.user.saveevaluates toundefined, and callingundefinedis a type problem, henceTypeError. The name of the error tells you which half of the expression to distrust.- You reload the page and none of your
console.logcalls appear, not even the first. What is the most likely cause? - A
SyntaxError, so the file never ran — Parsing happens before any execution, so a syntax problem anywhere in the file stops the whole file. Total silence is the signature. A runtime error, by contrast, lets everything above it run first.
Common mistakes
- Reading only the message and skipping the name. The name is the fastest half of the diagnosis.
- Debugging the top frame of the stack when it belongs to a library. Scroll to the first line of your own code.
- Patching
Cannot read properties of undefinedwith?.and calling it fixed, when the real question is why the value was missing.
Takeaways
- Every error has a name (what kind), a message (the specific complaint) and a stack (where).
ReferenceErrormeans the name is missing;TypeErrormeans the value cannot do what you asked.- A
SyntaxErrorprevents the whole file from running, so silence is the clue. - Log the suspect value before changing code, and search the message with your own identifiers removed.