Understanding JavaScript Errors

You open the browser console and see a wall of red text. The first step is to read the message and locate the source line.

Error messages provide information about what went wrong and where the problem occurred. Reading the error type, message, and stack trace gives you a practical starting point for debugging.

This guide will teach you to read JavaScript error messages, understand what they mean in plain English, and fix problems step by step.

What an error message actually is

When JavaScript encounters a problem while parsing or executing code, it can produce an Error object or another error value. The error type and message help you understand what went wrong. A typical error report also includes:

  • Error type - the category of the mistake (like TypeError or SyntaxError)
  • Message - a plain description of what went wrong
  • Stack trace - frames showing the call path that led to the error

Let's look at a real error:

Uncaught TypeError: Cannot read properties of undefined (reading 'name')
    at getUser (app.js:12:15)
    at main (app.js:24:3)

In plain English: "You tried to access .name on something that doesn't exist. This happened on line 12 of app.js, inside a function called getUser, which was called from line 24."

This is a common format, but error output varies by runtime and situation.

Opening the console

On any web page, press F12 (or Ctrl + Shift + J on Windows, Cmd + Option + J on Mac). Click the Console tab. That's where errors appear.

Anatomy of a console error

A typical browser console error may contain the error type, message, and stack-trace frames:

[Error Type]: [Message]
    at [function name] ([file]:[line]:[column])
    at [function name] ([file]:[line]:[column])
    ...

Here's how to break it down:

  • Line 1 - The error type and message. Read this first.
  • Lines below - Stack-trace frames. In a typical browser trace, the first relevant frame points to where the exception was thrown or surfaced, and later frames show callers that led there.
  • The blue link - Click the filename/line number on the right side. The console will take you directly to the exact line in your code.

Start with the first line and then inspect the relevant stack-trace frame. The message often identifies both the failed operation and the value involved.

What "Uncaught" means

Uncaught means the exception was not caught by an applicable error handler before it reached the top level of execution. In many synchronous cases, try...catch can be used to handle an exception. Other errors may be handled through mechanisms such as promise rejection handlers or framework-level error boundaries.

JavaScript provides several built-in error types. These are useful categories to recognize, with plain-English translations.

TypeError

What it means: You tried to use a value in a way that doesn't make sense for its type.

Most common message:

let user = undefined;
console.log(user.name);
// TypeError: Cannot read properties of undefined (reading 'name')

Plain English: "You assumed user had a .name property, but user is undefined. There's nothing there to read from."

Other common TypeError messages:

  • xyz is not a function - You tried to call something with () that isn't a function. Check for typos or wrong variable names.
  • Cannot set properties of null - You tried to change a property on something that is null. Usually means document.getElementById() returned null because the element doesn't exist yet.
  • xyz.map is not a function - You tried to use an array method on something that's not an array. Check what your variable actually holds.

ReferenceError

What it means: JavaScript could not resolve an identifier in the current execution context.

console.log(myName);
// ReferenceError: myName is not defined

Plain English: "JavaScript looked everywhere and can't find anything called myName. Either you forgot to declare it, misspelled it, or it's in a different scope."

Common causes:

  • Typos - userName vs username (JavaScript is case-sensitive)
  • Forgetting to declare with let, const, or var
  • Referencing a name outside the scope where it was declared
  • Using a variable before the line where it's declared (temporal dead zone with let/const)

SyntaxError

What it means: JavaScript cannot parse the code according to the language grammar.

let x = ;
// SyntaxError: Unexpected token ';'

function greet( {
// SyntaxError: Unexpected token '{'

Plain English: "Your code structure doesn't follow the rules. Something is missing or in the wrong place."

Common causes:

  • Missing closing brackets ), }, or ]
  • Missing commas in arrays or objects
  • Using a reserved word as a variable name
  • Forgetting quotes around strings

Key difference: A syntax error prevents the affected script from being parsed successfully, so that script cannot begin normal execution until the syntax problem is fixed. Other errors can occur while code is executing.

RangeError

What it means: A value is outside an allowed range for an operation.

let arr = new Array(-1);
// RangeError: Invalid array length

function forever() { forever(); }
forever();
// RangeError: Maximum call stack size exceeded

Plain English: The operation received a value outside its allowed range. Maximum call stack size exceeded can happen when recursive calls continue without reaching a base case, but recursion is not the only possible cause of a RangeError.

URIError

What it means: A URI-related function received an invalid encoded URI.

decodeURIComponent('%');
// URIError: URI malformed

This error is uncommon and can occur when URI encoding or decoding receives malformed input.

InternalError / EvalError

These errors are uncommon in typical browser application code. EvalError is rarely encountered in modern JavaScript, and InternalError is implementation-specific and is not part of the standard set of JavaScript error constructors supported uniformly across environments.

The stack trace is the part below the error message. It usually lists filenames, line numbers, and functions that show the call path that led to the error.

Uncaught TypeError: Cannot read properties of undefined (reading 'email')
    at renderProfile (profile.js:8:22)
    at updateUI (app.js:45:5)
    at handleClick (app.js:12:3)

How to read it: start from the top

In a typical browser stack trace, the first frame after the error message points to the location where the exception was thrown or surfaced - profile.js, line 8, column 22 in this example.

The following frames show callers that led there. In this example, handleClick called updateUI, which called renderProfile, where the exception surfaced.

What to do with it

  1. Open the top relevant frame, such as profile.js:8, in DevTools.
  2. Inspect the variables and scope at that line. Which value could be undefined?
  3. Move down the call stack, such as to app.js:45, and inspect what data was passed to renderProfile.
  4. Trace the value backward through the code until you find where the incorrect value was introduced.

Use the stack frames to connect the failing operation to the inputs that led to it.

Read the stack trace in two directions: start at the top to find the failing line, then move downward to understand the call path that brought execution there.

  1. Read the error: identify the type and bad value.
  2. Open the top frame: click the filename and line in DevTools.
  3. Trace inputs: move down the frames to find where the value entered the flow.
  4. Use a breakpoint: inspect Scope and step over the failing line.
  5. Verify the fix: reproduce the action and confirm the original trace disappears.
function renderProfile(profile) {
  debugger; // DevTools pauses here
  return profile.name.toUpperCase();
}

function updateUI(data) {
  return renderProfile(data.profile);
}

function handleClick() {
  return updateUI({ profile: undefined });
}

handleClick(); // Trace the call stack back through all three functions
Debugging connection: logs show what a value was; a breakpoint lets you inspect the whole scope and call stack at the moment the error happens.

Here are errors you'll actually hit as a beginner, translated into plain English, with the fix spelled out.

Error: "Cannot read properties of null"

// Your HTML:  <button id="submit-btn">Send</button>

// Your JavaScript (in <head>):
let btn = document.getElementById('submit-btn');
btn.addEventListener('click', sendForm);
// TypeError: Cannot read properties of null (reading 'addEventListener')

What happened: If the script runs before the target element has been parsed, getElementById() can return null because it cannot find the element yet.

Fix: Prefer defer for an external script, or place the script at the bottom of the body. You can also wait for DOMContentLoaded:

<script src="app.js" defer></script>
document.addEventListener('DOMContentLoaded', function() {
  let btn = document.getElementById('submit-btn');
  btn.addEventListener('click', sendForm);
});

Error: "xyz is not a function"

let score = 100;
score();
// TypeError: score is not a function

What happened: You put () after a variable that holds a number, not a function. JavaScript tried to "call" it and couldn't.

Common cause: You accidentally named a variable the same as a function, or you have a typo. Check what score actually is.

Error: "xyz is not defined"

function greet() {
  let message = "Hello!";
}
greet();
console.log(message);
// ReferenceError: message is not defined

What happened: message was created inside greet() with let, so it only exists inside that function. Outside the function, it doesn't exist.

Fix: Either return the value from the function, or declare the variable outside:

function greet() {
  return "Hello!";
}
let message = greet();
console.log(message); // "Hello!"

Error: "Unexpected token"

let fruits = ["apple", "banana" "cherry"];
// SyntaxError: Unexpected string

What happened: Missing comma between "banana" and "cherry". JavaScript sees two strings next to each other and doesn't understand what you want.

Fix: Add the comma: ["apple", "banana", "cherry"]

Error: "Maximum call stack size exceeded"

function countdown(n) {
  console.log(n);
  countdown(n - 1); // never stops!
}
countdown(5);
// RangeError: Maximum call stack size exceeded

What happened: The function keeps calling itself forever. There's no if check to stop the recursion.

Fix: Add a base case - a condition that stops the recursion:

function countdown(n) {
  if (n <= 0) return; // stop here!
  console.log(n);
  countdown(n - 1);
}

When you see an error, follow these five steps to identify the failing operation, trace its input, and verify the fix.

Step 1: Read the first line

Don't look at the stack trace yet. Just read the error type and message: TypeError: Cannot read properties of undefined (reading 'length'). Translate it to English: "Something is undefined and I tried to access .length on it."

Step 2: Click the file link

Click the blue filename:line link on the right side of the error. The browser opens the Sources panel and highlights the exact line. Now you can see which variable is the problem.

Step 3: Add a console.log() above the broken line

If it's not obvious what's wrong, add a console.log() right before the broken line to inspect the value:

console.log("data is:", data);  // What is it actually?
console.log(data.length);        // This is where it crashes

Save, refresh, and check the console. This shows what the variable actually contains, such as undefined or null.

Step 4: Trace it backwards

Now you know the variable is wrong. Ask: "Where did this variable get its value?" Go to that line and add another console.log(). Keep going backwards until you find where the data went wrong.

Step 5: Fix and test

Once you find the root cause, fix it, remove temporary console.log() statements, reproduce the original scenario, and verify the behavior. If a new error appears, repeat from step 1.

These aren't "error types" - they're situations that cause errors. Knowing them in advance saves you hours.

Trap 1: Script loads before HTML

If a script runs before the target element has been parsed, a getElementById() call can return null. This can happen when a script in the <head> executes without waiting for the document.

Fix: Put your <script> tag at the end of <body>, or use defer:

<script src="app.js" defer></script>

Trap 2: Using = instead of ===

This may not throw an error, but it can produce incorrect behavior:

// Bug: this ASSIGNS 5, doesn't compare
if (x = 5) {
  console.log("This always runs!");
}

// Fix: use === to compare
if (x === 5) {
  console.log("Only runs when x is 5");
}

Trap 3: Callback returns undefined

A common trap with map(), filter(), and other array methods:

let doubled = [1, 2, 3].map(function(num) {
  num * 2; // Missing return!
});
console.log(doubled); // [undefined, undefined, undefined]

Fix: Add return, or use an arrow function with implicit return:

let doubled = [1, 2, 3].map(num => num * 2);
// [2, 4, 6]

Trap 4: Async timing

Trying to use data from fetch() or setTimeout() before it arrives:

let data;
fetch('/api/users').then(res => res.json()).then(json => {
  data = json;
});
console.log(data); // undefined! The fetch hasn't finished yet

Why it happens: The fetch() request is asynchronous. The code immediately after fetch() can run before the response arrives, so data may still be undefined. Fix: Use the data inside the .then() callback, or use async/await.

Trap 5: Off-by-one with arrays

let colors = ["red", "green", "blue"];
console.log(colors[3]); // undefined (last index is 2, not 3)

Arrays start at index 0. An array with 3 items has indices 0, 1, 2. Accessing index 3 returns undefined, and trying to call a method on it causes a TypeError.

Beyond console.log(), there are a few tricks that can save you time:

Named logs

When you have multiple console.log() calls, label them so you know which is which:

console.log("user:", user);
console.log("response:", response);
// Much clearer than just: console.log(user);

console.table()

If you're logging an array of objects, console.table() displays it as a neat table:

let users = [
  { name: "Alice", age: 25 },
  { name: "Bob", age: 30 }
];
console.table(users);
// Shows a formatted table in the console

typeof check

When a value isn't what you expect, check its type:

console.log(typeof myVariable);
// "string", "number", "undefined", "object", etc.

console.trace()

Prints a stack trace at any point - even when there's no error. Useful when you want to know "how did the code get here?"

function processOrder(order) {
  console.trace("processOrder was called");
  // ... rest of function
}
  • Error messages provide the error type, message, and useful location information.
  • Read the first line to identify the error type and message, then open the relevant file link.
  • TypeError usually indicates an operation is being performed on an inappropriate value type.
  • ReferenceError usually indicates an identifier cannot be resolved.
  • SyntaxError indicates invalid JavaScript syntax.
  • RangeError indicates a value is outside an allowed range for an operation.
  • URIError relates to invalid URI encoding or decoding.
  • Stack traces help identify where an error occurred and how execution reached that point.
  • Use DevTools, breakpoints, logging, and stack traces to isolate problems.
  • Trace incorrect values backward to find the root cause, then reproduce the original scenario to verify the fix.
  • What is the difference between SyntaxError, ReferenceError, and TypeError?
  • How do you read a JavaScript stack trace to find the actual failing line?
  • Why does Cannot read properties of undefined happen so often, and how do you prevent it?
  • What is the fastest way to debug an error when a page appears blank?
  • How can console.log(), console.table(), and console.trace() help in different debugging scenarios?
  • Why is checking typeof useful before calling methods on a value?
  • What are common causes of ReferenceError in beginner code?
  • How do you systematically isolate a bug using a step-by-step process?
  • What is the difference between an error in your source file and an error in a third-party script?
  • How can small defensive checks reduce runtime errors in JavaScript projects?

Reviewed by

SimplyJavaScript Editorial Team

Technical editors and JavaScript educators with hands-on experience building frontend projects, writing learning material, and reviewing tutorials for clarity, accuracy, and beginner-friendly guidance.