name: debugging-toolkit description: | Debug Python and Node.js applications using pdb, debugpy, remote-pdb, node inspect, and Chrome DevTools Protocol. Covers local breakpoints, remote attach, post-mortem, and Hermes-specific processes. version: 1.0.0 platforms: [linux, macos, windows] metadata: hermes:
tags: [debugging, python, nodejs, pdb, debugpy, cdp, breakpoints]
Debugging Toolkit
Python Debugging
Three tools, pick by situation:
| Tool | When |
|---|---|
breakpoint() + pdb |
Local, interactive, simplest |
python -m pdb |
Launch script under debugger with no source edits |
debugpy / remote-pdb |
Remote / headless / attach to running process |
pdb Quick Reference
n next line (step over)
s step into
r return from current function
c continue
unt N continue until line N
j N jump to line N
l / ll list source / full function
w where (stack trace)
u / d up / down stack
p expr print expression
pp expr pretty-print
display expr auto-print on every stop
b file:line set breakpoint
b func break on function entry
interact drop into full Python REPL in current scope
q quit
Recipes
Local breakpoint:
def compute(x, y):
result = some_helper(x)
breakpoint() # drops into pdb here
return result + y
Remove before committing: rg -n 'breakpoint\(\)' --type py
Launch under pdb:
python -m pdb path/to/script.py arg1 arg2
Debug pytest test:
pytest tests/foo.py::test_bar --pdb -p no:xdist # xdist breaks pdb
Post-mortem:
import pdb, sys
try:
run_the_thing()
except Exception:
pdb.post_mortem(sys.exc_info()[2])
Remote debug with remote-pdb (recommended for agents):
pip install remote-pdb
from remote_pdb import set_trace
set_trace(host="127.0.0.1", port=4444) # blocks until connection
Then: nc 127.0.0.1 4444 → full (Pdb) prompt.
Remote debug with debugpy (IDE integration):
pip install debugpy
import debugpy
debugpy.listen(("127.0.0.1", 5678))
debugpy.wait_for_client()
debugpy.breakpoint()
Or launch: python -m debugpy --listen 127.0.0.1:5678 --wait-for-client script.py
Attach from VS Code with request: attach launch config.
Attach to running process:
python -m debugpy --listen 127.0.0.1:5678 --pid <pid>
May need: echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
Hermes-specific Python processes
- Tests: always add
-p no:xdistfor pdb - Gateway / tui_gateway: use
remote-pdbat handler entry - _SlashWorker:
set_trace()inside worker's exec path - Asyncio: pdb works in coroutines;
awaitinside pdb requires Python 3.13+
Node.js Debugging
Two tools:
| Tool | When |
|---|---|
node inspect |
Built-in CLI REPL, quick poking |
CDP via chrome-remote-interface |
Scriptable automation, non-interactive |
node inspect Quick Reference
node inspect path/to/script.js
node --inspect-brk $(which tsx) path/to/script.ts
c / cont continue
n / next step over
s / step step into
o / out step out
sb('file.js', 42) set breakpoint
cb('file.js', 42) clear breakpoint
bt backtrace
list(5) show 5 lines around current
repl drop into JS REPL in current scope
exec expr evaluate expression
restart restart script
.exit quit
Attaching to running process
kill -SIGUSR1 <pid> # enable inspector on existing process
node inspect -p <pid> # attach by PID
node inspect ws://127.0.0.1:9229/<uuid> # attach by URL
Start with inspector:
node --inspect script.js # listen on 9229, keep running
node --inspect-brk script.js # listen AND pause on first line
node --inspect=0.0.0.0:9230 script.js # custom host:port
TypeScript via tsx:
node --inspect-brk --import tsx script.ts
Programmatic CDP
npm i -g chrome-remote-interface
const CDP = require('chrome-remote-interface');
(async () => {
const client = await CDP({ port: 9229 });
const { Debugger, Runtime } = client;
Debugger.paused(async ({ callFrames }) => {
const top = callFrames[0];
console.log(`PAUSED @ ${top.url}:${top.location.lineNumber + 1}`);
// ... inspect scopes, evaluate expressions, resume
});
await Runtime.enable(); await Debugger.enable();
await Debugger.setBreakpointByUrl({ urlRegex: '.*app\\.tsx$', lineNumber: 119 });
await Runtime.runIfWaitingForDebugger();
})();
Hermes ui-tui debugging
Single component under dev:
cd ui-tui && npm run build
node --inspect-brk dist/entry.js
# In another terminal: node inspect -p <pid>
Running hermes --tui:
hermes --tui &
TUI_PID=$(pgrep -f 'ui-tui/dist/entry' | head -1)
kill -SIGUSR1 "$TUI_PID"
curl -s http://127.0.0.1:9229/json/list | jq -r '.[0].webSocketDebuggerUrl'
node inspect ws://127.0.0.1:9229/<uuid>
Vitest under debugger:
node --inspect-brk ./node_modules/vitest/vitest.mjs run --no-file-parallelism src/app/foo.test.tsx
Heap / CPU profiles (non-interactive)
// CPU profile
await client.Profiler.enable();
await client.Profiler.start();
await new Promise(r => setTimeout(r, 5000));
const { profile } = await client.Profiler.stop();
fs.writeFileSync('/tmp/cpu.cpuprofile', JSON.stringify(profile));
// Heap snapshot
await client.HeapProfiler.enable();
const chunks = [];
client.HeapProfiler.addHeapSnapshotChunk(({ chunk }) => chunks.push(chunk));
await client.HeapProfiler.takeHeapSnapshot({ reportProgress: false });
fs.writeFileSync('/tmp/heap.heapsnapshot', chunks.join(''));
Common Pitfalls
| Issue | Fix |
|---|---|
| pdb under pytest-xdist silently hangs | Use -p no:xdist or -n 0 |
breakpoint() in CI hangs |
Never commit breakpoints; pre-commit grep |
PYTHONBREAKPOINT=0 disables breakpoints |
Check env var |
debugpy without wait_for_client() races past first breakpoint |
Add wait_for_client() |
| Attach to PID fails on hardened kernels | echo 0 > /proc/sys/kernel/yama/ptrace_scope |
| Node breakpoints hit emitted JS not TS | Break in dist/*.js or enable sourcemaps |
--inspect vs --inspect-brk |
Use -brk when you need to set breakpoints before code runs |
| Port collisions on 9229 | Use --inspect=0 for random port |
--inspect on parent doesn't inspect children |
Use NODE_OPTIONS='--inspect-brk' |
Background node inspect target stays paused after Ctrl+C |
cont first or kill explicitly |
Agent Bridge Authentication Failures
When an agent bridge (e.g., Claude Code → Feishu) reports Agent error or SEND_TO_CC failure, the real error is usually an expired Claude API token, not a bridge bug. See <code>references/agent-bridge-auth-troubleshooting.md</code> for the full diagnostic and fix recipe.