Skip to content
Back to all articles
Developer5 min readOctober 2, 2026

How to Debug JavaScript with the Console

Read error messages properly, log values in ways that actually help, find the classic bugs (undefined, async order, equality), and test fixes quickly.

How to Debug JavaScript with the Console article
In this guide
  1. Read the error message first
  2. Log values in ways that help
  3. The bugs behind most problems
  4. Reproduce the bug in isolation
  5. A debugging routine that works
  6. Things to watch out for
  7. Frequently asked questions

Every JavaScript developer, from the first week to the tenth year, spends a lot of time asking the same question: why is my code not doing what I think it is doing? The fastest way to answer it is rarely to stare harder at the code. It is to make the code tell you what it is doing, and the console is how it talks.

This guide covers how to read JavaScript error messages, how to log values in ways that actually help, and how to recognise the handful of bugs that cause most problems. Every example can be pasted into the JavaScript Compiler and run with Ctrl+Enter.

Read the error message first

When JavaScript fails, it usually tells you why, in one line. Learning to read that line is the single biggest debugging skill. An error has three useful parts: the type, the message and the location.

ErrorWhat it usually means
ReferenceError: x is not definedYou used a variable that does not exist, often a typo or a variable declared in another scope
TypeError: Cannot read properties of undefinedYou tried to read a property of something that is undefined, such as user.name when user is undefined
TypeError: x is not a functionYou called something that is not a function, often a misspelled method or a value you expected to be a function
SyntaxError: Unexpected tokenThe code is not valid JavaScript, often a missing bracket, quote or comma just before the reported spot
RangeError: Maximum call stack size exceededA function keeps calling itself without stopping

The location points to where JavaScript noticed the problem, which is not always where the mistake is. "Cannot read properties of undefined" is reported where you read the property, but the real bug is wherever the value should have been set.

Log values in ways that help

console.log is the simplest debugging tool and still one of the best. A few habits make it far more useful:

Label your logs. A bare number in the console is meaningless ten logs later.

js
const total = 3 * 19.99;
console.log('total', total);
console.log({ total }); // the variable name becomes the label

Log objects, not strings. Logging an object shows its whole structure. The JavaScript Compiler prints objects and arrays as formatted JSON, so nested data is easy to read.

js
const order = { id: 42, items: [{ sku: 'A1', qty: 2 }], paid: false };
console.log('order', order);

Use warn and error for importance. console.warn and console.error stand out in the output, which helps when you are looking for the one line that matters.

Log before and after. When a value changes unexpectedly, log it before and after each step that touches it. The step where it changes is your bug.

The bugs behind most problems

Undefined values

Most "cannot read properties of undefined" errors come from data that is not there yet or not where you think it is:

js
const users = [{ name: 'Asha' }, { name: 'Ravi' }];
const user = users.find(u => u.name === 'Arjun');
console.log(user); // undefined: no match
console.log(user?.name ?? 'Not found'); // safe access with a fallback

Optional chaining (?.) and the nullish coalescing operator (??) prevent the crash, but ask why the value is missing as well.

Equality surprises

JavaScript's loose equality (==) converts types before comparing, with surprising results:

js
console.log(0 == ''); // true
console.log('1' == 1); // true
console.log(null == undefined); // true
console.log('1' === 1); // false

Use strict equality (===) by default. It compares both value and type.

Asynchronous order

Code that waits for something (a timer, a network request, a promise) runs later, not in the order it is written:

js
console.log('1: start');
setTimeout(() => console.log('3: timer'), 0);
Promise.resolve().then(() => console.log('2: promise'));
console.log('1b: end of script');

Run it and the order is start, end of script, promise, timer. If a value is undefined when you log it, check whether the code that sets it has actually run yet. With async functions, await the result before using it.

Scope and closures

Variables declared with let and const exist only inside the block where they are declared. Using var, or forgetting to declare a variable at all, causes values to leak or be shared in unexpected ways, a classic source of bugs in loops. Prefer const, and let when the value must change.

Reproduce the bug in isolation

When a bug appears inside a large application, copy the smallest piece of code that shows the problem into a clean environment. The JavaScript Compiler starts every run in a fresh, sandboxed frame, so nothing from a previous run or the rest of your app interferes. If the bug disappears in isolation, it comes from how the snippet interacts with the rest of your code. If it stays, you have a small example you can fix quickly, or share when asking for help.

For code that builds or changes a page, use the Live HTML Viewer, which shows the rendered page alongside its console. Our guide on testing HTML, CSS and JavaScript online covers that workflow.

A debugging routine that works

  1. Read the error message and note the type, message and line.
  2. Reproduce the problem reliably, ideally in a small snippet.
  3. Form a guess about the cause.
  4. Log to test the guess: the values going in, the values coming out.
  5. Change one thing and run again.
  6. Remove your debug logs when the bug is fixed, or turn them into proper error handling.

Changing several things at once is the most common way to "fix" a bug without understanding it, and to bring it back later.

Things to watch out for

  • Infinite loops freeze the browser tab, in any environment. While experimenting, add a counter that stops a loop after a set number of iterations.
  • Features vary by browser version. If something works for you but not for a user, compare browsers; What Is My Browser shows exact versions.
  • Do not paste code you do not understand into consoles on sites where you are logged in. The JavaScript Compiler runs code in an isolated sandbox that cannot read this site's cookies or storage, but other consoles may not be protected.

Frequently asked questions

Can I use async/await in the JavaScript Compiler?

Yes. Logs from asynchronous code appear when they run.

Why does my DOM code show nothing?

The compiler's sandbox has an empty page. Use the Live HTML Viewer for code that builds a page.

Can I import npm packages?

Not directly; it runs plain browser JavaScript.

Why does the error line number look off?

Code runs inside a small wrapper, so line numbers in uncaught errors can be approximate. Use labelled logs to narrow it down.

Debugging is a skill, not luck. Read the error, log with labels, reproduce in isolation and change one thing at a time. Paste the examples above into the JavaScript Compiler and watch the console explain your code back to you.

Was this article helpful?

Found this guide useful?

Share it with someone who is struggling with the same problem.

Free tool · No sign-up

Live JavaScript Compiler

Write and run JavaScript in your browser and see console output, warnings and errors instantly. Each run starts in a fresh, sandboxed environment. Free.

Open tool
Toolyconvert Team profile photo

Written by

Toolyconvert Team

Web Development & Software Engineering Team

The Toolyconvert Team builds, maintains, and improves free online tools designed to make everyday digital tasks faster and easier. Our team works across web development, software engineering, APIs, performance optimization, testing, and user experience to create reliable and practical tools.