Understanding JavaScript Errors

You open the browser console and see a wall of red text. Your heart rate spikes. You think, "I broke everything."

Relax. An error message is not a punishment - it's a clue. JavaScript is literally telling you what went wrong and where. Once you learn to read these messages, they become the most helpful thing on your screen.

This guide will teach you to read any JavaScript error message calmly, understand what it means in plain English, and fix it step by step. No jargon. No panic.

What an error message actually is

When JavaScript runs your code and hits something it can't handle, it stops and creates an Error object. That object has three parts:

  • Error type - the category of the mistake (like TypeError or SyntaxError)
  • Message - a plain description of what went wrong
  • Stack trace - the breadcrumb trail showing which lines of code led here

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."

That's it. Every error follows this same pattern. Once you see the pattern, you stop panicking.

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

Every red line in the console follows this structure:

[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 actual problem. Read this first. Always.
  • Lines below - The stack trace. Start from the top - that's where the error happened. Lines below show which functions called it.
  • 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.

Pro tip: The error message sometimes looks long and scary. Ignore everything after the first line until you understand the first line. That one sentence tells you 90% of the story.

What "Uncaught" means

You'll see the word Uncaught before most errors. It just means "nobody handled this error with a try/catch." For beginners, every error will say Uncaught - and that's perfectly normal. You can ignore this word and focus on what comes after it.

There are only a handful of error types in JavaScript. Here they are, ranked from most common to least, with plain-English translations.

1. 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.

2. ReferenceError

What it means: You used a variable or function name that JavaScript has never heard of.

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
  • Using a variable before the line where it's declared (temporal dead zone with let/const)

3. SyntaxError

What it means: Your code has a grammar mistake. JavaScript can't even start running it.

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: SyntaxError prevents your code from running at all. Other errors happen while it's running. If nothing in your file works, it's probably a SyntaxError.

4. RangeError

What it means: A number is outside the allowed range.

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

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

Plain English: "You asked for something that's out of bounds." The most common one is "Maximum call stack size exceeded" - which means a function is calling itself forever (infinite recursion).

5. URIError

What it means: You passed an invalid string to a URL-encoding function.

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

You'll see this one rarely. It means a URL was built incorrectly.

6. InternalError / EvalError

These are extremely rare. InternalError usually means the browser ran out of memory or resources. EvalError is essentially never thrown in modern JavaScript. Don't worry about either of these.

The stack trace is the part below the error message. It looks like a list of filenames and line numbers. Beginners usually ignore it - but it's actually a treasure map.

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

The first line after the error is where the problem actually happened - profile.js, line 8, column 22.

Each line below is "who called that function." So the story reads bottom-up: handleClick called updateUI, which called renderProfile, which crashed.

What to do with it

  1. Click the top filename link (e.g., profile.js:8) - the browser opens that exact line
  2. Look at line 8 - what variable could be undefined?
  3. If it's not obvious, click the second line (app.js:45) - check what data was passed to renderProfile
  4. Keep going down the chain until you find where the bad data came from

Think of it like detective work: the error tells you the crime scene, but the stack trace tells you who was involved and in what order.

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: The JavaScript runs before the HTML button exists on the page. getElementById returns null because it can't find the element yet.

Fix: Move your <script> tag to the bottom of the body, or wrap your code in DOMContentLoaded:

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);
}

Every time you see an error, follow these five steps. It works for beginners and it works for senior developers. The only difference is speed.

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. You'll see exactly what the variable holds (probably 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 your console.log() statements, and test again. If a new error appears, repeat from step 1. Each error you fix gets you closer.

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 your <script> tag is in the <head>, your JavaScript runs before the page's HTML has loaded. Any getElementById() call will return null.

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 doesn't throw an error - it's worse. It silently does the wrong thing:

// 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

Fix: Use your 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 are helpers, not punishments. They tell you exactly what's wrong and where.
  • Always read the first line of the error. That's the type and message - the 90% explanation.
  • Click the file link to jump to the exact line in your code.
  • TypeError = wrong type. Usually means something is undefined or null when you expected an object.
  • ReferenceError = variable doesn't exist. Check spelling, scope, and declaration.
  • SyntaxError = broken grammar. Missing brackets, commas, or quotes. Nothing runs until you fix it.
  • RangeError = value out of bounds. Most often: infinite recursion.
  • Stack traces read top to bottom - the top line is where it crashed, below is who called it.
  • Use the 5-step debug process: Read ? Click ? Log ? Trace ? Fix.
  • The more errors you see, the faster you get. Every experienced developer sees dozens of errors every day. The difference is they don't panic anymore.