Debug Node.js with node inspect and Chrome DevTools Protocol

Debug Node.js applications with node inspect and the Chrome DevTools Protocol, using breakpoints, heap snapshots, and CPU profiles to hunt down hard-to-reproduce issues.

OpenClaw Hermes Intermediate Copied: 0
1 resource

Requirements

  • Requires Node.js to be installed and available.

How to use

Reach for this when Node.js debugging needs inspector access: hidden locals, async hangs, flaky tests, child processes, startup races, memory growth, or CPU hot paths.

Default to node inspect first; turn to the Chrome DevTools Protocol only when you need scripted breakpoints, automated state capture, heap snapshots, or CPU profiles. Install tooling outside the repo unless the project already depends on it.

Pause on entry: node inspect path/to/script.js

TypeScript: node --inspect-brk --import tsx path/to/script.ts

Inspect target list: curl -s http://127.0.0.1:9229/json/list | jq

OpenClaw CLI path: node --inspect-brk openclaw.mjs ...

OpenClaw test path: OPENCLAW_VITEST_MAX_WORKERS=1 node --inspect-brk scripts/run-vitest.mjs <file>

Prefer 127.0.0.1 inspector binds, and do not expose --inspect=0.0.0.0 unless the network is isolated.

For Vitest, debug one file with one worker and avoid worker pools while stepping.

For TypeScript source breakpoints, use --enable-source-maps when useful; node inspect can still show emitted paths.

For child processes, NODE_OPTIONS=--inspect-brk can propagate the inspector, but each child needs its own port.

For long-lived gateway or dev processes, attach by PID after confirming the target with /json/list.

Install tooling outside the repo unless the project already depends on it.

mkdir -p /tmp/cdp-tools
npm --prefix /tmp/cdp-tools i chrome-remote-interface
NODE_PATH=/tmp/cdp-tools/node_modules node /tmp/cdp-debug.cjs

const CDP = require("chrome-remote-interface");

(async () => {
  const client = await CDP({ port: 9229 });
  const { Debugger, Runtime } = client;

  Debugger.paused(async ({ callFrames, reason }) => {
    const top = callFrames[0];
    console.log("paused", reason, top.url, top.location.lineNumber + 1);
    const { result } = await Debugger.evaluateOnCallFrame({
      callFrameId: top.callFrameId,
      expression: "JSON.stringify({ pid: process.pid })",
    });
    console.log(result.value ?? result.description);
    await Debugger.resume();
  });

  await Runtime.enable();
  await Debugger.enable();
  await Debugger.setBreakpointByUrl({ urlRegex: ".*target\\.js$", lineNumber: 41 });
  await Runtime.runIfWaitingForDebugger();
})();

Note that --inspect does not pause; use --inspect-brk when setup must happen before code runs.

The default port is 9229; use --inspect=0 or a unique port for parallel targets.

If a breakpoint misses, confirm the file path, source map behavior, and whether execution already passed the line.

If the process appears frozen after detaching, it may still be paused in the debugger.

Source: openclaw/openclaw and its contributors. License: MIT.

Copyright (c) 2026 OpenClaw Foundation

Translation and editorial adaptation by ClawCases.

>Resources

>Related Use Cases

>More at this level: Intermediate