Reading error messages
Learn what JavaScript error names, messages, and stack traces are telling you so you can find bugs faster and search for help well.
- 01Name the categoryTell SyntaxError, ReferenceError, TypeError, RangeError, URIError, and EvalError apart.
- 02Follow the breadcrumb trailRead a stack trace from the thrown line back through the calls that led there.
- 03Search like a debuggerTurn a noisy message into a useful query, then check the right docs or line of code.
Errors are clues, not insults
Every developer sees red error text. Beginners often read it as “you failed.” Professionals read it as evidence: what kind of problem happened, the best guess at where it happened, and the chain of function calls that led there.
An error report usually has three useful parts:
- The name, like
TypeErrororReferenceError. This is the category of mistake. - The message, like
Cannot read properties of undefined. This describes the operation that failed. Messages vary between browsers and runtimes. - The stack trace, a breadcrumb trail of function calls, newest first. It helps you jump to the line that actually threw, then trace back to the caller that passed bad data.
A dashboard light does not fix the car, but it narrows the search: brakes, oil, battery, engine. JavaScript errors do the same. The name points to a category; the message points to the failed action; the stack points to where to look first.
- In real life: Oil light
- In JavaScript:
TypeError: wrong kind of value for the operation - In real life: Check engine text
- In JavaScript:
error.message: the engine’s more specific clue - In real life: Mechanic tracing the hose or wire
- In JavaScript: You following the stack trace to your code
Where the analogy stops: A car light cannot tell you the exact code line. JavaScript often can, but minified files, browser differences, and async code can still make stacks imperfect.
- Read the error name and message out loud.
- Find the first stack frame that belongs to your code.
- Open that file, line, and column.
- Ask, “What value is this line assuming?”
This lesson builds on strict mode’s idea that mistakes should be loud, and on the console tools you met earlier. The next lesson, Debugging in the browser, will show breakpoints, stepping, and watches. Here we learn the skill that comes first: understanding what the error already says.
SyntaxError, ReferenceError & TypeError
INTERACTIVEThe three errors you will meet constantly are about grammar, names, and values.
| Error name | Plain-English meaning | Tiny example |
|---|---|---|
| SyntaxError | The engine or parser cannot read the grammar. | JSON.parse("{bad json}") throws while parsing JSON text. |
| ReferenceError | A name was used, but no binding with that name exists. | console.log(total) when total was never declared. |
| TypeError | A value exists, but the operation does not fit that value. | undefined.name or calling something that is not a function. |
Think of these as different questions: “Can JavaScript read this?”, “Does this name exist?”, and “Can this value do that?” Run the lab below. It catches each thrown error only so the page can show you error.name and error.message. The lesson on try, catch, and finally comes later; for now, treat catch as a safe display case around a thrown error.
const demos = { reference() { console.log(total); }, type() { const user = undefined; return user.name; }, range() { return new Array(-1); }, uri() { return decodeURIComponent("%"); }, syntaxAtRuntime() { return JSON.parse("{bad json}"); }, evalCompatibility() { throw new EvalError("EvalError still exists"); },}; function inspectError(runDemo) { try { runDemo(); } catch (error) { return { name: error.name, message: error.message, }; }}ReferenceErrortotal is not definedA ReferenceError means JavaScript looked up a name and found no variable, parameter, or global with that spelling.
ReferenceError: total is not defined
Notice that the name is stable and useful. The message is also useful, but not universal. Chrome and Node often say Cannot read properties of undefined (reading 'name'). Firefox phrases some messages differently. When a test or article relies on an exact message, it should say which engine it describes.
RangeError, URIError & EvalError
SORTThe next three are less common, but they explain many scary-looking messages.
- RangeError means a number is outside the allowed range.
new Array(-1)cannot create an array with a negative length. In Chrome and Node,(1).toFixed(101)saystoFixed() digits argument must be between 0 and 100. - URIError comes from URI encoding and decoding. A percent sign starts an encoded byte, so
decodeURIComponent("%")throwsURI malformed. - EvalError is mostly historical. Modern JavaScript no longer throws it for ordinary code. It exists for compatibility, and you can still create one with
new EvalError("message").
Sorting the mistake helps you choose the next question. If it is a ReferenceError, inspect spelling and scope. If it is a TypeError, inspect the value. If it is a RangeError, inspect the number you passed.
- In real life: Spelling a street that does not exist
- In JavaScript:
ReferenceError: unknown name - In real life: Using a house key on a bicycle lock
- In JavaScript:
TypeError: real thing, wrong operation - In real life: Entering floor -1 in an elevator
- In JavaScript:
RangeError: number outside allowed range - In real life: Writing an address with broken symbols
- In JavaScript:
URIError: malformed encoded text
Where the analogy stops: Real bugs can involve more than one idea. JavaScript reports the first error it actually hits, not a complete diagnosis of your program.
Sort these real messages by error family. A few messages come from JSON or built-in methods rather than source-code parsing, which is why reading the surrounding line matters.
- x is not defined
- Cannot read properties of undefined (reading 'name')
- Unexpected token
- Invalid array length
- Assignment to constant variable.
- Unexpected end of JSON input
- toFixed() digits argument must be between 0 and 100
Drag each message to the error name that usually owns it. If you are unsure, read the operation hidden inside the message.
- URI malformed
- URIError: malformed URI sequence
- Unexpected token
- Unexpected end of JSON input
These two both come from parsers reading text. Decide whether the text is URI-encoded data or JSON/code grammar.
Reading a stack trace
STEP THROUGHA stack trace is a receipt for function calls. It lists what was running when the error happened, with the newest call first. The top frame usually answers “where did it throw?” Lower frames answer “who called this?”
Imagine walking through rooms and dropping a breadcrumb in each one. When something breaks, the closest crumb is where you are now. The earlier crumbs show how you got there.
- In real life: Newest breadcrumb near your feet
- In JavaScript: Top stack frame: the thrown line
- In real life: Older crumbs lead back home
- In JavaScript: Lower frames: callers that led there
- In real life: A crumb with a street address
- In JavaScript:
file:line:columnin a stack frame
Where the analogy stops: Stack traces show calls, not intent. The thrown line can be innocent if a caller passed bad data.
Step through this tiny checkout bug. The order has a total, but no discount object. Watch the “Running in” context grow as each function calls the next.
Step through the breadcrumb trail. Watch the Running in context grow from script to placeOrder to calculateTotal to applyDiscount.
script
function placeOrder(order) { return calculateTotal(order);} function calculateTotal(order) { return applyDiscount(order.total, order.discount);} function applyDiscount(total, discount) { return total * (1 - discount.rate);} Now compare that guided replay to a real error.stack string from your browser. The exact text can differ, but the strategy stays the same: read the message, find the top frame in your code, and then inspect the value the line assumed.
function placeOrder(order) { return calculateTotal(order);} function calculateTotal(order) { return applyDiscount(order.total, order.discount);} function applyDiscount(total, discount) { return total * (1 - discount.rate);} placeOrder({ total: 40 });TypeError: Cannot read properties of undefined (reading 'rate')
at applyDiscount (cart.js:10:32)
at calculateTotal (cart.js:6:10)
at placeOrder (cart.js:2:10)
at cart.js:13:1The exact wording and stack format can differ. Chrome and Node use at function (file:line:column). Firefox often writes function@file:line:column.
Here is the same idea in a clearly labeled V8-style example. Chrome and Node use this shape. Firefox often writes frames like applyDiscount@cart.js:10:32 instead.
TypeError: Cannot read properties of undefined (reading 'rate') at applyDiscount (cart.js:10:32) at calculateTotal (cart.js:6:10) at placeOrder (cart.js:2:10) at cart.js:13:1TypeError: Cannot read properties of undefined (reading 'rate')The error name and message: start here.at applyDiscount (cart.js:10:32)Top frame: where the error was thrown.at calculateTotal (cart.js:6:10)Next frame: who called that function.at placeOrder (cart.js:2:10)Keep moving down until you find your own code.at cart.js:13:1The original top-level call.
Searching for answers
WORKFLOWSearch engines are useful only when your query is clean. Error messages often include your variable names, file names, and data. Keep the reusable phrase; remove your app’s private names.
const account = undefined;console.log(account.profile.name);- Copy the exact phrase:
Cannot read properties of undefined. - Put that phrase in quotes. Add
JavaScriptonly if the results are too broad. - Remove your own names, like
accountandprofile, unless the phrase alone is too vague. - Check MDN’s JavaScript error reference and the docs for the function in the top stack frame.
- Make a minimal reproduction: the smallest snippet that still throws. Then rubber-duck it by explaining the values line by line.
A good first query is "Cannot read properties of undefined". After you understand the category, come back to your code and ask the real debugging question: “Why is this value undefined here?”
Where you’ll use this
Error reading is a daily skill. When a page refuses to load, a button does nothing, or a test fails, the fastest path is usually:
- Open the console and find the first error after the action you just took.
- Read the name and message before changing code.
- Jump to the first stack frame in your file, not a browser extension or library bundle.
- Inspect the values on that line. Is a name misspelled? Is a property missing? Is a number out of range?
- If you still do not know, search the reusable phrase and create a smaller reproduction.
| You see | First question | Likely fix |
|---|---|---|
| ReferenceError | Did I spell and declare this name? | Fix the name, import, or scope. |
| TypeError | What value is actually here? | Guard missing data or pass the right shape. |
| SyntaxError | What parser is complaining? | Fix JavaScript grammar or malformed JSON. |
| RangeError | Which number is outside the allowed range? | Clamp, validate, or change the argument. |
Common misconceptions
“The top stack frame always contains the real bug.”
It contains the thrown line. The real cause may be a caller that passed undefined, an empty string, or a bad number.
“TypeError means I wrote the wrong type annotation.”
Plain JavaScript has no type annotations. TypeError means a runtime value was used in an operation it cannot perform.
“SyntaxError always means the JavaScript file could not start.”
Often yes, but parsers inside functions can throw SyntaxError at runtime too, like JSON.parse("{bad json}").
“If the message differs from a tutorial, I have a different bug.”
Maybe, but engines phrase messages differently. Compare the error name and failed operation first.
“Searching the whole message is always best.”
Remove your private variable names first. Search the exact shared phrase, then add details if needed.
JavaScript’s standard defines error constructors like TypeError and RangeError, but it does not force every engine to use the same English message text.
Practice: read the error before fixing
5 EXERCISESRead the code. What error name does it print?
try {
console.log(total);
} catch (error) {
console.log(error.name);
}The snippet catches the error only to print its name. Because total is not defined, the printed name is ReferenceError.
This is not a spelling problem and not a property problem. What error name does the snippet print?
try {
(1).toFixed(101);
} catch (error) {
console.log(error.name);
}The number exists and toFixed exists, but the digits argument is outside the allowed range. The snippet prints RangeError.
In the checkout stack trace, which function is the top frame where the error is thrown?
function placeOrder(order) {
return calculateTotal(order);
}
function calculateTotal(order) {
return applyDiscount(order.total, order.discount);
}
function applyDiscount(total, discount) {
return total * (1 - discount.rate);
}
placeOrder({ total: 40 });The first thrown operation is reading discount.rate, which happens inside applyDiscount. calculateTotal and placeOrder are callers lower in the stack.
Change the object so the program prints a name instead of throwing. What exact name does the solution print?
const account = {};
console.log(account.profile.name);const account = { profile: { name: "Ada Lovelace" } };
console.log(account.profile.name);The error happened because account.profile was undefined. Adding the missing object shape makes account.profile.name real, so the program prints Ada Lovelace.
You see TypeError: Cannot read properties of undefined (reading 'name'). Write the exact phrase you would search first.
A strong first search is "Cannot read properties of undefined". It keeps the shared phrase, removes app-specific names, and points you to explanations of the TypeError pattern.
Quiz: check your understanding
7 QUESTIONSThese questions mix concepts with tiny programs. Every wrong answer explains the trap, so use the feedback as part of the lesson.
Question 1 of 7What is the first thing to read in an error report?
Choose an answer to see the explanation.
Question 2 of 7What error name does the missing-name snippet print?
Read the code, then predicttry { console.log(total); } catch (error) { console.log(error.name); }Choose an answer to see the explanation.
Question 3 of 7What error name does the undefined-property snippet print?
Read the code, then predicttry { const user = undefined; console.log(user.name); } catch (error) { console.log(error.name); }Choose an answer to see the explanation.
Question 4 of 7What error name does the JSON parsing snippet print?
Read the code, then predicttry { JSON.parse("{bad json}"); } catch (error) { console.log(error.name); }Choose an answer to see the explanation.
Question 5 of 7In a stack trace, where is the thrown line usually shown?
Choose an answer to see the explanation.
Question 6 of 7What is the best first search query for
Cannot read properties of undefined (reading 'profile')?Choose an answer to see the explanation.
Question 7 of 7Which statement about EvalError is accurate?
Choose an answer to see the explanation.
Key takeaways
- Read the error name, then the message, then the stack trace.
SyntaxErroris grammar,ReferenceErroris a missing name, andTypeErroris a real value used the wrong way.RangeErrormeans an argument is outside an allowed range;URIErrormeans malformed URI text;EvalErrormostly exists for compatibility.- Stack traces are newest first. Find the first frame in your code and inspect the value on that line.
- Search exact reusable phrases in quotes, then check MDN or the docs for the function in the top frame.
One-line definition.
An error message is JavaScript’s report of what failed; a stack trace is the breadcrumb trail of calls that led to it.
Up next: Debugging in the browser.
console.log(total);const user = undefined;user.name;