JavaScript Debugging

Why should you care about JavaScript Debugging?

Debugging is the skill that turns confusion into clarity. The faster you can find and fix bugs, the faster you can ship features with confidence.

Debugging means finding and fixing errors (bugs) in your code. Every developer runs into bugs, and learning how to squash them quickly is one of the most useful skills you can build.

A bug might be a typo, a logic mistake, or behavior you did not expect. Debugging tools help you pause execution, inspect values, and understand what your code is doing step by step.

Bugs usually fall into three broad groups: a syntax error stops JavaScript from understanding the code, a runtime error happens while the code is running, and a logic error lets the code run but produces the wrong result. Logic errors often do not appear as red console errors, so compare the actual result with the result you expected.

simply JavaScript Debugging flow - find bug, inspect with DevTools, fix code, test

javascript

function add(a, b) {
  return a - b; // bug: should be a + b
}

console.log(add(3, 4)); // Output: -1 (wrong, expected 7)

In the example above, a simple typo (- instead of +) causes the wrong result. Debugging helps you catch and correct these issues.

Chrome comes with a built-in toolkit called Developer Tools (DevTools). To open it, just press F12 or right-click anywhere on a page and choose Inspect.

The most useful panels for JavaScript debugging are:

  • Console: view log messages, run JavaScript, and see errors.
  • Sources: view your source files, set breakpoints, and step through code.
  • Network: inspect HTTP requests and responses.
  • Elements: inspect and modify the HTML and CSS of the page.

For JavaScript debugging, the Console and Sources panels are your best friends.

DevTools changes the current page while you inspect it, but those temporary edits do not automatically change your source files. Make the fix in your editor, reload the page, and test the behavior again.

simply JavaScript  flow - find bug, inspect with DevTools, fix code, test

A breakpoint is like a stop sign for your code. You place it on a specific line, and when the browser reaches that line, everything pauses so you can look around and check your variables.

To add a breakpoint in Chrome DevTools:

  • Open DevTools (F12) and go to the Sources panel.
  • Open your JavaScript file from the file tree on the left.
  • Click the line number where you want to pause. A blue marker appears.
  • Reload the page or trigger the action. The code will pause at your breakpoint.

You can also add a breakpoint directly in your code using the debugger statement:

simply JavaScript  flow - find bug, inspect with DevTools, fix code, test

javascript

function calculateTotal(price, tax) {
  debugger; // execution pauses here when DevTools is open
  let total = price + tax;
  return total;
}

calculateTotal(100, 18);

The debugger statement pauses code only when DevTools is open. If DevTools is closed, it is ignored.

Use a repeatable loop instead of changing random lines until the bug disappears:

  1. Reproduce: write down the exact action and input that causes the bug.
  2. Describe: compare the expected result with the actual result.
  3. Pause: set a breakpoint before the wrong result is produced.
  4. Inspect: check the call stack, local variables, and relevant object properties.
  5. Step: use Step over to move line by line, Step into to enter a function, and Step out to leave the current function.
  6. Hypothesize: change one small thing that could explain the evidence.
  7. Verify: rerun the original reproduction and a nearby normal case.
  8. Prevent: add a test, validation, or clearer code so the bug does not return.

Watch expressions let you track a value while execution is paused. In DevTools Sources, find the Watch panel, select +, and enter an expression such as cart.length, user?.role, or total > limit. The expression updates after each step.

javascript

function applyDiscount(items, limit) {
  let total = 0;

  for (const item of items) {
    total += item.price;
    debugger;
  }

  return total > limit ? total * 0.9 : total;
}

applyDiscount([{ price: 20 }, { price: 30 }], 40);
// Watch: total, item.price, total > limit

Useful habit: remove temporary console.log() calls after the investigation, but keep a deliberate breakpoint or test only when it continues to help future development.

When something breaks, the browser Console shows an error with a file name and line number. Read that message carefully first. It usually points you directly to the issue.

Steps to fix an error:

  • Open DevTools and go to the Console tab.
  • Read the error message and note the file name and line number.
  • Click on the file link in the error to jump to that line in the Sources panel.
  • Inspect the code and look for mistakes like typos, wrong variable names, missing brackets, etc.
  • Fix the code and reload the page to verify the error is gone.
simply JavaScript  flow - find bug, inspect with DevTools, fix code, test

javascript

// Error example
console.log(myVar); // ReferenceError: myVar is not defined

// Fix: declare the variable before using it
let myVar = "Hello";
console.log(myVar); // Output: Hello

console.log() prints values to the browser console, whether they are strings, numbers, objects, or arrays. Log useful labels and intermediate values so you can see how data changes as the program runs.

simply JavaScript  flow - find bug, inspect with DevTools, fix code, test

javascript

let endpoint = "/api/orders";
let age = 25;

console.log(endpoint);        // /api/orders
console.log(age);             // 25
console.log(endpoint, age);   // /api/orders 25

let request = { url: "/api/orders", method: "GET" };
console.log(request);        // { url: "/api/orders", method: "GET" }

You can pass multiple values to console.log() separated by commas, and all of them will be printed on the same line.

console.warn() prints a warning message to the console, usually with a warning icon or color. The exact appearance depends on the browser. Use it for things that are not broken yet but might cause trouble later.

simply JavaScript  flow - find bug, inspect with DevTools, fix code, test

javascript

let age = -5;

if (age < 0) {
  console.warn("Age should not be negative:", age);
}
// Output (in yellow): Age should not be negative: -5

console.error() reports an error message in the console, often with an icon or stack information. It does not stop the program or create a thrown exception by itself; use throw when you need to stop execution and signal an error.

simply JavaScript  flow - find bug, inspect with DevTools, fix code, test

javascript

function fetchData(url) {
  if (!url) {
    console.error("Error: URL is required to fetch data.");
    return;
  }
  // proceed with fetch...
}

fetchData(); // Output (in red): Error: URL is required to fetch data.

console.table() takes your arrays or objects and displays them as a clean, readable table in the console. Instead of squinting at nested data, you get nice rows and columns.

simply JavaScript  flow - find bug, inspect with DevTools, fix code, test

javascript

const orders = [
  { id: "ORD-001", total: 59.99 },
  { id: "ORD-002", total: 124.50 },
  { id: "ORD-003", total: 89.00 },
];

console.table(orders);
// Displays a formatted table with columns: id, total

This is much easier to read than console.log(users) when you have an array of objects.

Fixing bugs gets a lot easier when you follow a clear process. Here are the steps that'll save you time.

Identifying the Bug

First, you need to spot that something is off. Maybe there's an error on screen, a red message in the console, or the output just looks wrong. Ask yourself: what did I expect to happen, and what actually happened?

simply JavaScript  flow - find bug, inspect with DevTools, fix code, test

javascript

// Expected: multiply(3, 4) should return 12
// Actual: it returns 7
function multiply(a, b) {
  return a + b; // bug identified: using + instead of *
}

Finding the Bug

Now that you know something is wrong, track down exactly where the problem lives. Use console.log(), breakpoints, or the DevTools Sources panel to follow your code's path and check variable values.

javascript

function multiply(a, b) {
  console.log("a:", a, "b:", b); // log inputs to trace the issue
  return a + b;
}

console.log(multiply(3, 4)); // Output: a: 3 b: 4 then 7 (reveals the bug)

Fixing the Bug

Once you've found the problem, fix it! Make a small, focused change and test to make sure it works without breaking anything else.

javascript

function multiply(a, b) {
  return a * b; // fixed: changed + to *
}

console.log(multiply(3, 4)); // Output: 12

Not Repeating Bugs

Once you've fixed the bug, take a moment to ask: why did this happen? Write a test for it, add a comment, or clean up the code so it's less confusing. That way, the same bug won't sneak back in.

My story: Early in my career, I used console.log everywhere and refreshed constantly. It worked, but it was slow and frustrating. Then a senior developer showed me breakpoints in Chrome DevTools. In 10 minutes, I learned how to pause code and inspect values line by line. That small shift saved me countless hours. If you learn one thing deeply in debugging, learn the Sources panel and breakpoints.

JavaScript has several built-in error types. Knowing them helps you quickly understand what went wrong.

Syntax Error

You'll get a SyntaxError when your code has a typo or is written in a way JavaScript can't understand. Your code won't run at all until you fix it.

simply JavaScript  flow - find bug, inspect with DevTools, fix code, test

javascript

// Missing closing parenthesis
console.log("Hello"
// SyntaxError: Unexpected end of input

Reference Error

A ReferenceError appears when JavaScript cannot access a variable or function by the name you used. This can happen because it was never declared, is outside the current scope, or is being accessed in the Temporal Dead Zone before its declaration.

javascript

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

Type Error

A TypeError means you tried to do something a value doesn't support, like calling .toUpperCase() on a number or reading a property from null.

javascript

let num = 42;
num.toUpperCase();
// TypeError: num.toUpperCase is not a function

let obj = null;
console.log(obj.name);
// TypeError: Cannot read properties of null

InternalError and stack overflow

InternalError is a non-standard error type used by some JavaScript engines. In browsers such as Chrome, infinite recursion usually appears as a RangeError with a message such as "Maximum call stack size exceeded." The underlying problem is that function calls have filled the call stack.

javascript

function recurse() {
  return recurse(); // calls itself forever
}

recurse();
// InternalError: too much recursion (in Firefox)
// RangeError: Maximum call stack size exceeded (in Chrome)

Range Error

A RangeError shows up when you pass a value that's outside the allowed range, like trying to create an array with a negative length.

javascript

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

let num = 3.14159;
console.log(num.toFixed(200));
// RangeError: toFixed() digits argument must be between 0 and 100

URI Error

A URIError happens when you pass a badly formatted URI to functions like decodeURIComponent().

javascript

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

Off-by-one loop

If a loop runs once too many or too few times, inspect the starting value, the boundary operator, and the update step.

javascript

const scores = [10, 20, 30];

// Bug: index 3 is outside the array, so the last log is undefined.
for (let i = 0; i <= scores.length; i++) {
  console.log(scores[i]);
}

// Fix: the last valid index is length - 1.
for (let i = 0; i < scores.length; i++) {
  console.log(scores[i]);
}

Stale or missing DOM element

A selector can return null when the ID is misspelled or the script runs before the element exists. Inspect the selector result before calling a method on it.

javascript

const button = document.querySelector("#save-btn");

// Bug: button is null if the page uses id="save-button".
button.addEventListener("click", save);

// Debug: inspect the selector result and fix the matching ID.
console.log({ button });

Async value arrives later

A request does not return its data immediately. If a variable is still undefined, set a breakpoint inside the callback or after await, not only where the request starts.

javascript

let user;

fetch("/api/user")
  .then(response => response.json())
  .then(data => {
    user = data;
    console.log("Ready:", user.name);
  });

console.log(user); // Bug: this runs before the response arrives

Debug by location in time: ask whether the problem happens before the function runs, inside it, after it returns, or later when an async result arrives. That narrows the breakpoint quickly.

  • Debugging is finding and fixing errors in code.
  • Chrome DevTools provides Console, Sources, Network, and Elements panels to help debug.
  • A breakpoint pauses code execution at a specific line so you can inspect state.
  • The debugger statement adds a breakpoint directly in code.
  • Use Step over, Step into, Step out, and Watch expressions to follow changing state.
  • console.log() prints values for inspection.
  • console.warn() prints a yellow warning message.
  • console.error() prints a red error message with a stack trace.
  • console.table() displays arrays and objects as a table.
  • Bug fixing steps: Identify, Find, Fix, Prevent recurrence.
  • SyntaxError: invalid code syntax.
  • ReferenceError: using an undeclared variable.
  • TypeError: wrong type operation.
  • InternalError: engine-level error (e.g., stack overflow).
  • RangeError: value out of allowed range.
  • URIError: malformed URI in URI functions.

What's next? Now that you know how to find and fix bugs, let's move on to working with numbers and dates in the next tutorial.

No videos are currently listed for this topic. Please use the resources and interview questions above for guided debugging practice.

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.