Computer Programming 1 (JavaScript) • Debugging, Testing & Code Quality

Interpreting Errors — I can interpret JavaScript error messages/stack traces to locate where a problem occurs.

Learn to read JavaScript error messages like a detective reads clues to track down bugs fast.

A JavaScript data file with code brackets and a laptop.
Inside this lesson 10 sections 5 questions 5 figures

SECTION 1

Historical Context & Motivation

Every programmer, from beginner to expert, encounters errors. In the earliest days of computing, finding a bug meant physically inspecting hardware — the term "bug" itself traces back to a real moth found stuck inside a relay at Harvard in 1947. As programming languages evolved, so did the tools for reporting what went wrong. Early programs would simply crash with no explanation, leaving developers to guess. Modern JavaScript engines, by contrast, provide rich error messages and stack traces that pinpoint exactly where something broke. Understanding these messages is one of the most practical skills you can build as a developer.

1947 • The First "Bug"

Grace Hopper's team documents an actual moth causing a computer malfunction. The practice of "debugging" enters the programmer's vocabulary.

1995 • JavaScript Is Born

Brendan Eich creates JavaScript in just 10 days. Early error reporting is minimal — browsers often silently ignore mistakes or show vague alerts.

2006 • Firebug Launches

The Firebug extension for Firefox introduces a proper developer console, allowing programmers to view error messages and stack traces in the browser for the first time.

2008 • Chrome DevTools & V8

Google Chrome ships with built-in DevTools and the V8 engine, which produces detailed, fast error messages with line numbers and stack traces.

2020s • Modern Debugging Ecosystem

Today's browsers, Node.js, and code editors provide color-coded error messages, clickable stack traces, and AI-powered suggestions for common mistakes.

Despite these improvements, many students still panic when they see a red error message in the console. The key question this lesson addresses is: How do you systematically read an error message and use a stack trace to find and fix the exact line of code that caused a problem?

SECTION 2

Core Principles & Definitions

Before you can fix a bug, you need to understand what JavaScript is telling you. Every error message follows a predictable structure, and once you learn to recognize the pattern, reading errors becomes almost automatic. Let's break down the foundational ideas.

1

Error Type

The category of mistake, such as SyntaxError, ReferenceError, or TypeError. This tells you the general nature of the problem before you read any details.
2

Error Message

A short, human-readable description that follows the error type. For example: x is not defined. This narrows down what specifically went wrong.
3

Stack Trace

A list of function calls that were active when the error occurred, printed from most recent (top) to oldest (bottom). Each line shows a file name, line number, and column number.
4

Line & Column Number

The exact position in your source code where JavaScript detected the error, written as filename.js:12:5 meaning line 12, column 5. This is your treasure map to the bug.
5

Call Stack

The sequence of functions that were called leading up to the error. The call stack helps you trace how your program arrived at the broken line of code, even if the real cause is somewhere earlier.
✦

KEY TAKEAWAY

Think of an error message like a GPS notification when you've taken a wrong turn. The error type is the category ("wrong turn"), the error message is the specific instruction ("make a U-turn"), and the stack trace is the list of turns you made that got you here. Together, they lead you straight back to the problem.

SECTION 3

Anatomy of a JavaScript Error

The diagram below breaks down a real JavaScript error message into its labeled parts. When you see an error in the browser console or a terminal, you'll be able to identify each component and know exactly where to look in your code.

ANATOMY OF A JAVASCRIPT ERROR MESSAGETypeError: Cannot read properties of undefined (reading 'name')① ERROR TYPE② ERROR MESSAGE at getStudentName (app.js:8:22) at displayRoster (app.js:15:10) at Object.<anonymous> (app.js:20:1) at Module._compile (node:internal/modules/cjs/loader:1364:14) at Module._extensions..js (node:internal/modules/cjs/loader:1422:10)③ STACK TRACE④ FILE : LINE : COLUMNHOW TO READ THE STACKTop line = where error was thrownLines below = functions that called itGrayed lines = internal engine code (ignore)⑤ TIP: Start at the TOPThe first line of YOUR code is the best place to look first.
This diagram labels the five key parts of a JavaScript error: ① Error Type, ② Error Message, ③ Stack Trace, ④ File:Line:Column, and the reading strategy of starting at the top.

Notice how the stack trace reads like a story from top to bottom. The top line, at getStudentName (app.js:8:22), is where the error actually happened — the engine tried to access .name on something that was undefined. The second line tells you that displayRoster on line 15 called getStudentName. The grayed-out lines at the bottom are Node.js internal code — you can safely ignore those when debugging your own programs.

SECTION 4

How JavaScript Errors Work Under the Hood

JavaScript is a single-threaded language, meaning it executes one operation at a time. When your code runs, the engine maintains a data structure called the call stack — a last-in, first-out (LIFO) list that tracks which function is currently running and which functions called it. Every time a function is invoked, it gets "pushed" onto the stack. When the function finishes, it gets "popped" off.

When an error occurs, the JavaScript engine captures a snapshot of the entire call stack at that moment. This snapshot becomes the stack trace you see in the console. The engine then looks for a try...catch block that can handle the error. If none is found, the error "bubbles up" through the call stack and crashes your program, printing the full trace.

The Three Phases of an Error

HOW THE CALL STACK BUILDS AND AN ERROR PROPAGATESPHASE 1: Stack BuildsPHASE 2: Error ThrownPHASE 3: Stack Unwindsmain()displayRoster()getStudentName()Functions push onto stack↑ grows upmain()displayRoster()💥 getStudentName()TypeError thrown here!main()displayRoster()getStudentName()Error bubbles upNo try/catch → crashThe stack trace is a snapshot taken at Phase 2, printed in top-to-bottom order (most recent call first).
This diagram illustrates the three phases: the call stack builds as functions are called, the error is thrown at the top of the stack, and then the stack unwinds as the error bubbles up looking for a catch block.

Understanding this mechanism is powerful because it explains why the stack trace is ordered the way it is. The function at the top of the trace is always the one that was running when the error occurred. The functions below it are the chain of callers that led to that point. When you're debugging, start at the top of the stack trace, find the first line that references your own code (not library or engine internals), and open that file at the specified line number.

SECTION 5

Common JavaScript Error Types

JavaScript has several built-in error types, each pointing to a different category of mistake. Recognizing which type you're dealing with immediately narrows down what went wrong. Let's examine the most common ones you'll encounter.

Error TypeWhat It MeansCommon CauseExample
SyntaxErrorYour code violates the grammar rules of JavaScript. The engine cannot even parse it.Missing bracket, parenthesis, or quotation mark; typo in a keyword.SyntaxError: Unexpected token }
ReferenceErrorYou used a variable or function name that doesn't exist in the current scope.Misspelled variable name; using a variable before declaring it.ReferenceError: scroe is not defined
TypeErrorYou performed an operation on a value of the wrong type (e.g., calling something that isn't a function).Accessing a property on undefined or null.TypeError: x.toUpperCase is not a function
RangeErrorA value is outside the acceptable range.Infinite recursion (call stack overflow); invalid array length.RangeError: Maximum call stack size exceeded
URIErrorA global URI function was used incorrectly.Passing a malformed URI to decodeURIComponent().URIError: URI malformed

The five most common JavaScript error types and how to recognize them.

⚠

Pro Tip: SyntaxErrors Are Different

A SyntaxError is detected before your code runs, during the parsing phase. This means you won't get a stack trace — just a file name and line number. All other errors happen at runtime, while your code is executing, so they include a full stack trace.

The most frequently encountered errors for beginners are SyntaxError, ReferenceError, and TypeError. If you memorize what these three mean, you'll be able to diagnose the vast majority of bugs in your programs. Over time, you'll start recognizing patterns: for instance, Cannot read properties of undefined almost always means you forgot to check whether a variable had a value before using it.

SECTION 6

Worked Example: Debugging a Real Error

Let's walk through a real scenario. Imagine you have written a small program that calculates the average score for a list of students, but when you run it, you see this error in the console:

📝

Console Output

TypeError: Cannot read properties of undefined (reading 'length') at calculateAverage (grades.js:4:26) at printReport (grades.js:10:18) at Object.<anonymous> (grades.js:15:1)

Debugging a TypeError Step by Step

  1. 1

    Step 1 — Read the Error Type

    The error begins with TypeError. This tells us we tried to perform an operation on a value that has the wrong type. We didn't break any grammar rules (that would be a SyntaxError), and we didn't use an undeclared variable (that would be a ReferenceError).
    Category: TypeError → wrong type used in an operation
  2. 2

    Step 2 — Read the Error Message

    The message says Cannot read properties of undefined (reading 'length'). This means we tried to access .length on something that was undefined. We expected an array or string, but got undefined instead.
    Clue: Some variable is undefined when it should be an array
  3. 3

    Step 3 — Read the Stack Trace (Top Line First)

    The first line of the stack trace says at calculateAverage (grades.js:4:26). This tells us the error occurred in the function calculateAverage, in the file grades.js, on line 4, at column 26. We now know exactly where to look.
    Location: grades.js, line 4, column 26
  4. 4

    Step 4 — Examine the Code at That Line

    Opening grades.js and going to line 4, we find: let total = scores.reduce((a, b) => a + b, 0) / scores.length;. Column 26 points to scores.length. The variable scores must be undefined. Looking at the function signature: function calculateAverage(scores) — the parameter scores is not being passed correctly.
    Root cause: scores parameter is undefined
  5. 5

    Step 5 — Trace Back Through the Call Chain

    The second line of the stack trace says at printReport (grades.js:10:18). Going to line 10, we find: let avg = calculateAverage(student.score);. The problem is clear — the property should be student.scores (plural), not student.score. A simple typo caused the entire crash!
    Fix: Change student.score to student.scores on line 10
✦

KEY TAKEAWAY

Notice how the actual typo was on line 10, but the error was reported on line 4. This is extremely common — the place where the error is thrown is often not the place where the real mistake lives. That's why reading the full stack trace is so important: it lets you trace the problem back to its true source.

SECTION 7

Debugging Strategies & Common Pitfalls

Now that you know how to read error messages, let's compare different strategies for tracking down bugs. Some approaches work better than others, and knowing when to use each one will save you a lot of time.

StrategyStrengthsLimitations
Read the error message firstFastest approach; often gives you the answer directly. Costs zero extra effort.Sometimes the message is cryptic or the real bug is earlier in the code, not at the reported line.
Add console.log() statementsEasy to use; lets you inspect variable values at specific points in your code.Clutters your code; can be slow for complex bugs; must remember to remove them later.
Use browser DevTools debuggerLets you set breakpoints, step through code line by line, and inspect all variables in scope.Steeper learning curve; can be overwhelming for beginners.
Rubber duck debuggingExplaining your code out loud forces you to think through each line and often reveals the mistake.Doesn't help when the problem is a language behavior you don't yet understand.
Search the error message onlineMillions of developers have encountered the same error; Stack Overflow often has the exact answer.You might copy a fix without understanding why it works, leading to fragile code.

Comparison of common debugging strategies for JavaScript.

🔧

THE DEBUGGING WORKFLOW

The most effective workflow combines strategies in order: ① Read the error message → ② Go to the reported line → ③ Trace back through the stack → ④ Add console.log() if needed → ⑤ Use the debugger for tough cases. Think of it like a doctor: first read the symptoms (error), then check the specific area (line), and only order advanced tests (debugger) when simpler methods don't reveal the issue.

SECTION 8

Connection to Advanced Debugging

The error-reading skills you've learned here are the foundation for more advanced debugging techniques you'll encounter as you grow as a programmer. Professional developers use the same fundamental process — read, locate, trace — but with more powerful tools and in more complex environments.

ConceptWhat You Know NowWhat Comes Next
Error handlingReading error messages after they crash your program.Using try...catch blocks to gracefully handle errors before they crash.
Stack tracesReading synchronous stack traces in the console.Reading asynchronous stack traces from Promises and async/await code, which can span multiple event loop cycles.
Debugging toolsconsole.log() and reading error output.Setting breakpoints, using watch expressions, and profiling performance with browser DevTools.
Error typesRecognizing built-in error types like TypeError and SyntaxError.Creating custom error classes with class MyError extends Error for your own libraries and applications.
TestingFixing bugs after they happen.Writing automated tests that catch bugs before they reach users, using frameworks like Jest or Mocha.

How your current error-reading skills connect to advanced debugging concepts.

As you advance, you'll also encounter errors in environments beyond the browser console — server logs, CI/CD pipelines, error monitoring dashboards, and mobile app crash reports. The core skill of reading an error type, message, and stack trace remains exactly the same in all of these contexts. Mastering it now gives you a transferable skill that applies to every programming language and framework you'll ever use.

SECTION 9

Practice Problems

Test your understanding of JavaScript error messages and stack traces with these five problems. Each one increases in difficulty. Try to answer before peeking at the solution!

Question 1: CONCEPTUAL

You see the following error in your console: ReferenceError: userName is not defined. In your own words, explain what type of mistake caused this error. What is the most likely thing you should check in your code?
Show answer
A ReferenceError means you used a variable name that JavaScript cannot find in any accessible scope. The most likely cause is a typo — you may have written userName but declared the variable as username (different capitalization) or user_name. You should check that the variable is declared (with let, const, or var) and that the spelling matches exactly, including capitalization.

Question 2: BASIC CALCULATION

Given this error: SyntaxError: Unexpected token ')' at script.js:7:22, identify: (a) the error type, (b) the file name, (c) the line number, and (d) the column number. Does this error include a stack trace? Why or why not?
Show answer
(a) The error type is SyntaxError. (b) The file name is script.js. (c) The line number is 7. (d) The column number is 22. This error does not include a stack trace because SyntaxErrors are caught during the parsing phase, before any code executes. Since the call stack hasn't been built yet, there is no stack to trace. You should go directly to line 7 and look for a mismatched or extra parenthesis near column 22.

Question 3: INTERMEDIATE

You see this error and stack trace: TypeError: items.map is not a function at formatList (utils.js:12:18) at renderPage (app.js:30:5) at Object.<anonymous> (app.js:45:1) The function formatList expects an array. What is the most likely reason items.map is not a function? Which line in which file should you check to find the real cause, and why?
Show answer
The error says items.map is not a function, which means items is not an array — it could be a string, number, object, undefined, or null. While the error was thrown at utils.js:12, the real cause is likely at app.js:30, which is where renderPage calls formatList and passes the items argument. You should check line 30 of app.js to see what value is actually being passed. The argument may come from data that was fetched incorrectly or is in an unexpected format.

Question 4: APPLIED

You're building a weather app. When a user searches for a city, the app fetches data from an API. For some cities, the app works fine, but for others it crashes with: TypeError: Cannot read properties of undefined (reading 'temp') at weather.js:22:35. Line 22 reads: let temperature = data.main.temp;. Explain what's happening and propose a fix that prevents the crash.
Show answer
The error means data.main is undefined for some cities. This likely happens when the API returns a response that doesn't include a main property — perhaps the city was not found, and the API returned an error object instead of weather data. To fix this, you should add a check before accessing nested properties: if (data && data.main) { let temperature = data.main.temp; } else { console.log('City data not available'); }. Alternatively, you can use optional chaining: let temperature = data?.main?.temp;. This prevents the crash by returning undefined safely instead of throwing an error.

Question 5: CRITICAL THINKING

Consider a scenario where you see this error: RangeError: Maximum call stack size exceeded at factorial (math.js:2:12) at factorial (math.js:2:12) at factorial (math.js:2:12) ... (repeats thousands of times) Line 2 of math.js reads: return n * factorial(n - 1);. (a) Explain why the stack trace contains the same function repeated thousands of times. (b) What is missing from this code, and how does that relate to the error type being RangeError rather than TypeError or ReferenceError?
Show answer
(a) The stack trace repeats because the function factorial calls itself recursively, and each call adds a new frame to the call stack. The stack trace is a snapshot of all those frames at the moment the engine ran out of memory. (b) The function is missing a base case — a condition like if (n <= 1) return 1; that stops the recursion. Without it, the function calls itself infinitely, each call consuming stack space until the engine's maximum stack size is reached. This is why it's a RangeError (a value exceeded an acceptable range — the stack size) rather than a TypeError or ReferenceError. The fix is to add: function factorial(n) { if (n <= 1) return 1; return n * factorial(n - 1); }. This illustrates how the error type gives you crucial context: RangeError + 'call stack size exceeded' = infinite recursion.

SUMMARY

Lesson Summary

Summary

JavaScript error messages are structured, readable signals — not punishments. Every error has three key parts: the error type (SyntaxError, ReferenceError, TypeError, or RangeError) that categorizes the mistake, the error message that describes what went wrong, and the stack trace that shows the file name, line number, and column number where the problem was detected, along with the chain of function calls that led there.

The most effective debugging workflow is to read the error message first, then go to the reported line, and then trace back through the call stack to find the true source of the bug. Remember that the line where the error is thrown is often not the line where the real mistake lives — the stack trace lets you follow the trail. With practice, reading errors becomes second nature, and you'll spend less time guessing and more time building.

Varsity Tutors • Computer Programming 1 (JavaScript) • Interpreting Errors