# Node.js Debugger and Inspector: Debug JavaScript with Chrome DevTools

Source: https://devdocs10.com/articles/javascript/js-node-debugger  
By Murat Bo · Published 2026-07-24 · DevDocs10 (https://devdocs10.com)

Node.js debugging lets you pause running JavaScript, inspect variables, step through code, and find the exact line where a bug happens. The inspector is the modern debugging interface that connects Node.js to tools like Chrome DevTools and other compatible debuggers.

> **Quick answer:** Start Node.js with `--inspect` or `--inspect-brk`, then connect a debugger to the reported port. Use `--inspect-brk` when you want execution to pause before your code runs.

> **Difficulty:** Beginner
>
> **You'll understand this better if you know:** basic JavaScript syntax, how to run Node.js programs from the terminal, and what variables, functions, and errors are.

## 1. What Is the Node.js Debugger and Inspector?

The Node.js debugger and inspector are tools for examining a running Node.js process. They let you pause execution, move through code one statement at a time, and inspect values without adding extra `console.log()` statements everywhere.

- `--inspect` starts the inspector so a debugger can attach while the program runs.
- `--inspect-brk` starts the inspector and pauses on the first line before any application code executes.
- Breakpoints stop execution at chosen lines so you can inspect scope, call stacks, and object values.
- The inspector protocol is what modern tools use to communicate with Node.js.

Older Node.js workflows used a command-line debugger, but the inspector is now the standard way to debug most Node.js applications.

## 2. Why the Node.js Inspector Matters

Debugging is easier when you can see the program in motion instead of guessing from logs. The inspector helps you understand state at the moment a problem occurs, which is especially useful for asynchronous code, event handlers, and data transformations.

It matters because many Node.js bugs are timing-related or depend on hidden state. A debugger lets you answer questions like:

- What value did this function actually receive?
- Which branch of the code ran?
- Why is an object missing a property here?
- Where did this error originate before it was caught?

Use the inspector when logs are not enough, when you need to compare expected and actual values, or when a bug only appears under specific conditions.

## 3. Basic Syntax

### Starting Node.js with the inspector

The simplest way to enable debugging is to launch your script with an inspector flag. This opens a debug port and waits for a debugger to connect.

```bash
node --inspect app.js
```

This starts the program normally and exposes the inspector on a local port. If you want Node.js to stop immediately before running your code, use `--inspect-brk` instead.

```bash
node --inspect-brk app.js
```

### How the connection works

When the process starts, Node.js prints a WebSocket debug address such as a localhost port. A compatible debugger attaches to that port and sends commands like “pause,” “resume,” and “step over.”

### Minimal example script

Here is a tiny program that we can inspect in the debugger.

```javascript
function greet(name) {
  const message = `Hello, ${name}`;
  return message;
}

const result = greet("Ada");
console.log(result);
```

Output (tested with Node 24.21.0):

```text
Hello, Ada
```

With a breakpoint inside `greet()`, you can inspect `name`, `message`, and the current call stack before the function returns.

## 4. Step-by-Step Examples

### Example 1: Debugging a simple function

Start by pausing on the first line, then step into the function call. This is the best way to learn how the debugger follows execution.

```bash
node --inspect-brk app.js
```

Once the debugger is attached, set a breakpoint on the `return` line and inspect the values in scope. You can confirm whether the function receives the argument you expected.

### Example 2: Finding a wrong calculation

Suppose a total is off by a small amount. A breakpoint lets you check each intermediate value instead of only seeing the final wrong result.

```javascript
function calculateTotal(price, taxRate) {
  const tax = price * taxRate;
  const total = price + tax;
  return total;
}
```

Stop at the line that computes `tax` and inspect both inputs. If `taxRate` is already incorrect, the bug is earlier in the call path.

### Example 3: Inspecting asynchronous code

Async bugs often happen between callbacks, promises, or timers. The inspector helps you pause at the point where a value becomes unexpected.

```javascript
async function loadUser(id) {
  const response = await fetchUser(id);
  return response.json();
}
```

Step through the `await` boundary and inspect the resolved value. This is useful when an API response shape differs from what your code expects.

### Example 4: Debugging an error path

Breakpoints are also useful around error handling. You can pause right before an exception is thrown and inspect the values that trigger it.

```javascript
function parseAge(value) {
  if (!value) {
    throw new Error("Age is required");
  }

  return Number(value);
}
```

By pausing before the `throw`, you can verify whether the function received `undefined`, an empty string, or an unexpected type.

## 5. Practical Use Cases

- Tracking down a failing route handler in a Node.js HTTP server.
- Inspecting the parsed output of a JSON file or API response.
- Stepping through business logic that transforms data before saving it.
- Debugging asynchronous code that behaves differently in development and production.
- Finding the exact line that produces a runtime exception or rejected promise.

The inspector is most helpful when the problem depends on runtime values rather than syntax alone.

## 6. Common Mistakes

### Mistake 1: Using `console.log()` instead of pausing the program

Logging is useful, but it can hide timing issues and make it hard to inspect nested objects at the moment of failure. Debugging is easier when you pause execution and inspect values directly.

**Problem:** The program keeps running, so you only see printed snapshots instead of the exact state at the bug location.

```javascript
console.log("value:", data);
doWork(data);
```

Output (tested with Node 24.21.0):

```text
Uncaught ReferenceError: data is not defined
```

**Fix:** Pause on a breakpoint or start with `--inspect-brk` so you can inspect the live state.

```javascript
// Set a breakpoint here in your editor or DevTools
const result = doWork(data);
```

The corrected approach works because you can inspect values before they change.

### Mistake 2: Forgetting to pause on the first line

If your code runs too quickly, the debugger may attach after the important code has already executed. That makes it seem like breakpoints are not working.

**Problem:** Without `--inspect-brk`, the program may finish or pass the important line before you attach the debugger.

```bash
node --inspect app.js
```

**Fix:** Use `--inspect-brk` when you need time to attach before application code runs.

```bash
node --inspect-brk app.js
```

This works because Node.js pauses before the first statement and waits for the debugger.

### Mistake 3: Setting breakpoints on code that is not loaded

Breakpoints only work in files that the running process has actually loaded. If a file is never imported or the path is wrong, the breakpoint will remain unbound.

**Problem:** The debugger cannot stop in code that the current process has not executed or loaded yet.

```javascript
const helper = require("./helpers/math.js");
// but the file on disk is actually ./helper/math.js
```

**Fix:** Verify the import path and confirm the file is part of the running process.

```javascript
const helper = require("./helper/math.js");
```

Once the correct file is loaded, the breakpoint becomes active and execution can stop there.

### Mistake 4: Assuming async code will pause exactly like sync code

Async functions split execution into separate steps, so a breakpoint before `await` does not behave the same as one after it. Beginners often think the debugger skipped a line when it actually resumed later.

**Problem:** The debugger pauses before the promise resolves, then continues in a later turn of the event loop.

```javascript
async function loadData() {
  const response = await fetch("/api/data");
  return await response.json();
}
```

**Fix:** Place breakpoints on both sides of the `await` and inspect the resolved value carefully.

```javascript
async function loadData() {
  const response = await fetch("/api/data");
  // Inspect response here before parsing
  return response.json();
}
```

The corrected version helps you see which step introduces the wrong value.

## 7. Best Practices

### Prefer `--inspect-brk` for startup bugs

If the bug happens during initialization, attach before the first line runs. This avoids missing one-time setup code and makes startup state easier to inspect.

```bash
node --inspect-brk server.js
```

This is better than attaching later because initialization code often runs only once.

### Use meaningful breakpoint locations

Break on lines that represent decisions, transformations, or data boundaries rather than every single statement. That keeps the debugging session focused.

```javascript
function normalizeEmail(email) {
  const trimmed = email.trim();
  return trimmed.toLowerCase();
}
```

Setting a breakpoint on the return line is usually more helpful than stopping on every intermediate assignment.

### Inspect objects instead of copying values into logs

The debugger can expand objects and show nested fields without formatting them manually. That is especially useful for large API responses and class instances.

```javascript
const user = {
  id: 42,
  profile: {
    name: "Ada",
    active: true
  }
};
```

This is more reliable than building long debug strings, and it gives you access to the real runtime object.

## 8. Limitations and Edge Cases

- The inspector listens on a local debug port, so you must secure or restrict access when exposing it beyond your machine.
- Code running in child processes or worker threads may need separate inspector attachment or extra configuration.
- Source maps matter when you debug transpiled code, because the runtime file may not match the original source line numbers.
- If your program exits immediately, you may need `--inspect-brk` to keep it paused long enough to attach.
- Breakpoints can appear unbound when files are loaded lazily, bundled, or referenced through a path that differs from what the editor expects.

**Warning:** Do not expose the Node.js inspector port on an untrusted network. Debug access can reveal process state and may allow code execution in the running process.

## 9. Practical Mini Project

Let’s debug a small Node.js script that processes a list of orders and computes a total. The goal is to find why one order is producing the wrong result.

```javascript
function calculateOrderTotal(items) {
  let total = 0;

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

  return total;
}

const orders = [
  { price: 10, quantity: 2 },
  { price: 5, quantity: 3 }
];

console.log(calculateOrderTotal(orders));
```

Output (tested with Node 24.21.0):

```text
35
```

Run it with the inspector, attach your debugger, and pause inside the loop. Inspect `item.price`, `item.quantity`, and `total` after each iteration. If the result is wrong, you can determine whether the bug is in the data or in the calculation logic.

This example shows the main debugging workflow: start with the inspector, pause where the logic matters, inspect live values, and then step forward until the source of the bug is clear.

## 10. Key Points

- The Node.js inspector lets you pause and inspect live code instead of guessing from logs.
- `--inspect` enables debugging, while `--inspect-brk` pauses before the first line.
- Breakpoints, stepping, and scope inspection are the core tools you will use most often.
- Async code, startup code, and file-loading issues are common places where debugging becomes especially valuable.
- Source maps, correct paths, and secure port handling matter when debugging real projects.

## 11. Practice Exercise

Try debugging this script to find why it prints the wrong average.

- Run the script with the inspector.
- Pause inside the calculation function.
- Inspect the array length and sum values.
- Fix the bug so the average is correct.

**Expected output:** The program should print `20` for the sample data.

**Hint:** Watch the division carefully and make sure you are using the right total.

```javascript
function average(values) {
  let sum = 0;

  for (const value of values) {
    sum += value;
  }

  return sum / values.length;
}

const numbers = [10, 20, 30];
console.log(average(numbers));
```

Output (tested with Node 24.21.0):

```text
20
```

## 12. Final Summary

The Node.js debugger and inspector are essential tools for understanding what your code is doing while it runs. They let you pause execution, inspect variables, follow the call stack, and track down bugs that are difficult to see with logs alone.

In practice, the most useful commands are `--inspect` and `--inspect-brk`. Use them to attach a debugger, set breakpoints in meaningful places, and step through both synchronous and asynchronous code with confidence.

Once you are comfortable with the inspector, the next step is to learn how source maps and IDE integrations make debugging transpiled or bundled JavaScript easier.
