Agent skill
bug-killer-sequenzia-agent-alchemy
Systematic, hypothesis-driven debugging workflow with triage-based track routing. Use when asked to "fix this bug", "debug this", "why is this failing", "this is broken", "investigate this error", "track down this issue", or any debugging situation. Supports --deep flag to force full investigation.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/bug-killer-sequenzia-agent-alchemy
SKILL.md
Bug Killer — Hypothesis-Driven Debugging Workflow
Execute a systematic debugging workflow that enforces investigation before fixes. Every bug gets a hypothesis journal, evidence gathering, and root cause confirmation before any code changes.
CRITICAL: Complete ALL 5 phases. The workflow is not complete until Phase 5: Wrap-up & Report is finished. After completing each phase, immediately proceed to the next phase without waiting for user prompts.
Phase Overview
- Triage & Reproduction — Understand, reproduce, route to quick or deep track
- Investigation — Gather evidence with language-specific techniques
- Root Cause Analysis — Confirm root cause through hypothesis testing
- Fix & Verify — Fix with proof, regression test, quality check
- Wrap-up & Report — Document trail, capture learnings
Phase 1: Triage & Reproduction
Goal: Understand the bug, reproduce it, and decide the investigation track.
1.1 Parse Context
Extract from $ARGUMENTS and conversation context:
- Bug description: What's failing? Error messages, symptoms
- Reproduction steps: How to trigger the bug (test command, user action, etc.)
- Environment: Language, framework, test runner, relevant config
- Prior attempts: Has the user already tried fixes? What didn't work?
- Deep flag: If
--deepis present, skip triage and go directly to deep track (jump to Phase 2 deep track)
1.2 Reproduce the Bug
Attempt to reproduce before investigating:
- If a failing test was mentioned, run it:
bash
# Run the specific test to confirm the failure <test-runner> <test-file>::<test-name> - If an error was described, find and trigger it
- If neither, search for related test files and run them
Capture the exact error output — this is your primary evidence.
If the bug cannot be reproduced:
- Ask the user for more context via the
questiontool - Check if it's environment-specific or intermittent
- Note "not yet reproduced" in the hypothesis journal
1.3 Form Initial Hypothesis
Based on the error message and context, form your first hypothesis:
### H1: [Title]
- Hypothesis: [What you think is causing the bug]
- Evidence for: [What supports this — error message, stack trace, etc.]
- Evidence against: [Anything that contradicts it — if none yet, say "None yet"]
- Test plan: [Specific steps to confirm or reject]
- Status: Pending
1.4 Route to Track
Quick-fix signals (ALL must be true):
- Clear, specific error message pointing to exact location
- Localized to 1-2 files (not spread across the codebase)
- Obvious fix visible from reading the error location
- No concurrency, timing, or state management involved
Deep-track signals (ANY one triggers deep track):
- Bug spans 3+ files or modules
- Root cause unclear from the error message alone
- Intermittent or environment-dependent failure
- Involves concurrency, timing, shared state, or async behavior
- User already tried fixes that didn't work
- Generic error message (e.g., "null reference" without clear origin)
- Stack trace points to library/framework code rather than application code
Present your assessment via the question tool:
- Summarize the bug and your initial hypothesis
- Recommend quick or deep track with justification
- Options: "Quick track (Recommended)" / "Deep track" / "Deep track" / "Quick track" — depending on your assessment
- Let the user override your recommendation
Track escalation rule: If during quick track execution, 2 hypotheses are rejected, automatically escalate to deep track. Preserve all hypothesis journal entries when escalating.
Phase 2: Investigation
Goal: Gather evidence systematically, guided by language-specific techniques.
2.1 Load Language Reference
Detect the primary language of the bug's context and load the appropriate reference inline from the sections below.
| Language | Reference Section |
|---|---|
| Python | See "Python Debugging Reference" section below |
| TypeScript / JavaScript | See "TypeScript/JavaScript Debugging Reference" section below |
| Other / Multiple | See "General Debugging Reference" section below |
Always also reference the "General Debugging Reference" section as a supplement when using a language-specific section.
2.2 Quick Track Investigation
For quick-track bugs, investigate directly:
- Read the error location — the file and function where the error occurs
- Read the immediate callers — 1-2 files up the call chain
- Check recent changes —
git log --oneline -5 -- <file>for the affected files - Update hypothesis — does the evidence support H1? Add evidence for/against
Proceed to Phase 3 (quick track).
2.3 Deep Track Investigation
For deep-track bugs, use parallel exploration agents:
-
Plan exploration areas — identify 2-3 focus areas based on the bug:
- Focus 1: The error site and immediate code path
- Focus 2: Data flow and state management leading to the error
- Focus 3: Related subsystems, configuration, or external dependencies
-
Launch code-explorer agents:
Spawn 2-3 code-explorer agents using the
tasktool withcommand: "code-explorer":Use task tool with command: "code-explorer" Prompt for each agent: Bug context: [description of the bug and error] Focus area: [specific area for this agent] Investigate this focus area in relation to the bug: - Find all relevant files - Trace the execution/data path - Identify where behavior diverges from expected - Note any suspicious patterns, recent changes, or known issues - Report structured findingsLaunch agents in parallel for independent focus areas.
-
Synthesize exploration results:
- Collect findings from all agents
- Identify convergence (multiple agents pointing to same area)
- Update hypothesis journal with new evidence
- Form additional hypotheses if evidence warrants (aim for 2-3 total)
Proceed to Phase 3 (deep track).
Phase 3: Root Cause Analysis
Goal: Confirm the root cause through systematic hypothesis testing.
3.1 Quick Track Root Cause
For quick-track bugs:
-
Verify the hypothesis:
- Read the specific code identified in Phase 2
- Trace the logic step-by-step
- Confirm that the hypothesized cause produces the observed error
-
If confirmed (Status → Confirmed):
- Update H1 with confirming evidence
- Proceed to Phase 4
-
If rejected (Status → Rejected):
- Update H1 with evidence against and reason for rejection
- Form a new hypothesis (H2) based on what you learned
- Investigate H2 following Phase 2 quick track steps
- If H2 is also rejected → escalate to deep track
- Preserve all journal entries, continue with Phase 2 deep track
3.2 Deep Track Root Cause
For deep-track bugs:
-
Prepare hypotheses for testing:
- You should have 2-3 hypotheses from Phase 2
- Each needs a concrete test plan (how to confirm or reject)
-
Launch bug-investigator agents:
Spawn 1-3 bug-investigator agents using the
tasktool withcommand: "bug-investigator"to test hypotheses in parallel:Use task tool with command: "bug-investigator" Prompt for each agent: Bug context: [description of the bug and error] Hypothesis to test: [specific hypothesis] Test plan: 1. [Step 1 — e.g., run this specific test with these arguments] 2. [Step 2 — e.g., check git blame for this function] 3. [Step 3 — e.g., trace the data from input to error site] Report your findings with verdict (confirmed/rejected/inconclusive), evidence, and recommendations.Launch agents in parallel when they test independent hypotheses.
-
Evaluate results:
-
Update hypothesis journal with each agent's findings
-
If one hypothesis is confirmed → proceed to Phase 4
-
If all are rejected/inconclusive → apply 5 Whys technique:
Take the strongest "inconclusive" finding and ask "why?" iteratively:
Observed: [what actually happens] Why? → [first-level cause] Why? → [second-level cause] Why? → [root cause] -
Form new hypotheses from 5 Whys analysis and repeat investigation
-
-
If stuck after 2 rounds of investigation:
- Present all findings to the user via the
questiontool - Share the hypothesis journal
- Ask for additional context or direction
- Options: "Continue investigating", "Try a different angle", "Provide more context"
- Present all findings to the user via the
Phase 4: Fix & Verify
Goal: Fix the root cause and prove the fix works.
4.1 Design the Fix
Before writing any code:
- Explain the root cause — state clearly what's wrong and why
- Explain the fix — describe what will change and WHY it addresses the root cause
- Identify affected files — list every file that needs modification
- Consider side effects — could this fix break other behavior?
4.2 Implement the Fix
- Read all files that will be modified before making changes
- Apply the fix using the
edittool — minimal, focused changes - Match existing patterns — follow the codebase's conventions
4.3 Run Tests
-
Run the originally failing test — it should now pass:
bash<test-runner> <test-file>::<test-name> -
Run related tests — tests in the same file and nearby test files:
bash<test-runner> <test-directory> -
If tests fail:
- Determine if the failure is related to the fix or pre-existing
- If related, revise the fix (do NOT revert to a different approach without updating the hypothesis journal)
- If pre-existing, note it but don't let it block the fix
4.4 Write Regression Test
Write a test that would have caught this bug:
- The test should fail WITHOUT the fix (verifying it tests the right thing)
- The test should pass WITH the fix
- The test should be minimal — test the specific behavior that was broken
- Place it in the appropriate test file following project conventions
4.5 Deep Track: Quality Check and Related Issues
Deep track only — skip on quick track.
-
Load code-quality skill:
Invoke: skill({ name: "code-quality" })
Review the fix against code quality principles.
-
Check for related issues:
- Search for the same pattern elsewhere in the codebase:
Grep for the pattern that caused the bug - If the same bug exists in other locations, report them to the user
- Ask via the
questiontool: "Fix all related instances now?" / "Fix only the reported bug" / "Create tasks for related fixes"
- Search for the same pattern elsewhere in the codebase:
Phase 5: Wrap-up & Report
Goal: Document the investigation trail and capture learnings.
5.1 Bug Fix Summary
Present to the user:
## Bug Fix Summary
### Bug
[One-line description of the bug]
### Root Cause
[What was actually wrong and why]
### Fix Applied
[What was changed, with file:line references]
### Tests
- [Originally failing test]: Now passing
- [Regression test added]: [test name and location]
- [Related tests]: All passing
### Track
[Quick / Deep] [Escalated from quick: Yes/No]
5.2 Hypothesis Journal Recap
Present the complete hypothesis journal showing the investigation trail:
### Investigation Trail
#### H1: [Title]
- Status: Confirmed / Rejected
- [Key evidence summary]
#### H2: [Title] (if applicable)
- Status: Confirmed / Rejected
- [Key evidence summary]
[... additional hypotheses ...]
5.3 Project Learnings
Load the project-learnings skill to evaluate whether this bug reveals project-specific knowledge worth capturing:
Invoke: skill({ name: "project-learnings" })
Follow its workflow to evaluate the finding. Common debugging discoveries that qualify:
- Surprising API behavior specific to this project
- Undocumented conventions that caused the bug
- Architectural constraints that aren't obvious from the code
5.4 Deep Track: Future Recommendations
Deep track only:
If the investigation revealed broader concerns, present recommendations:
- Architecture improvements to prevent similar bugs
- Missing test coverage areas
- Documentation gaps
- Monitoring or alerting suggestions
5.5 Next Steps
Offer the user options via the question tool:
- "Commit the fix" — proceed to commit workflow
- "Review the changes" — show a diff of all modifications
- "Run full test suite" — run the complete test suite to verify no regressions
- "Done" — wrap up the session
Hypothesis Journal
The hypothesis journal is the core artifact of this workflow. Maintain it throughout all phases.
Format
## Hypothesis Journal — [Bug Title]
### H1: [Descriptive Title]
- **Hypothesis:** [What's causing the bug — be specific]
- **Evidence for:** [Supporting observations with file:line references]
- **Evidence against:** [Contradicting observations]
- **Test plan:** [Concrete steps to confirm or reject]
- **Status:** Pending / Confirmed / Rejected
- **Notes:** [Additional context, timestamps, agent findings]
### H2: [Descriptive Title]
[Same format]
Rules
- Minimum hypotheses: 1 on quick track, 2-3 on deep track
- Never delete entries — rejected hypotheses are valuable context
- Update incrementally — add evidence as you find it, don't wait
- Be specific — "the data is wrong" is not a hypothesis; "processOrder receives dollars but expects cents" is
Track Reference
| Aspect | Quick Track | Deep Track |
|---|---|---|
| Investigation | Read error location + 1-2 callers | 2-3 code-explorer agents in parallel |
| Hypotheses | Minimum 1 | Minimum 2-3 |
| Root cause testing | Manual verification | 1-3 bug-investigator agents in parallel |
| Fix validation | Run failing + related tests | Tests + code-quality skill + related issue scan |
| Auto-escalation | After 2 rejected hypotheses | N/A |
| Typical complexity | Off-by-one, typo, wrong argument, missing null check | Race condition, state corruption, multi-file logic error |
Agent Coordination
Code Explorers (Phase 2, deep track)
Use the task tool with command: "code-explorer":
task({
command: "code-explorer",
prompt: "...",
description: "Explore [focus area] for bug investigation"
})
These are Sonnet-model read-only agents that explore codebase areas. Give each a distinct focus area related to the bug. They report structured findings.
Bug Investigators (Phase 3, deep track)
Use the task tool with command: "bug-investigator":
task({
command: "bug-investigator",
prompt: "...",
description: "Test hypothesis: [hypothesis title]"
})
These are Sonnet-model agents with bash access for running tests and git commands, but no write/edit — they investigate and report evidence, they don't fix code. Give each a specific hypothesis to test.
Error Handling
- If an agent fails, continue with remaining agents' results
- If all agents fail in a phase, fall back to manual investigation
- Never block on a single agent — partial results are better than no results
Error Recovery
If any phase fails:
- Explain what went wrong and what you've learned so far
- Present the hypothesis journal as-is
- Ask the user how to proceed via the
questiontool:- "Retry this phase"
- "Skip to fix" (if you have enough evidence)
- "Provide more context"
- "Abort"
Python Debugging Reference
Language-specific debugging techniques, common pitfalls, and diagnostic tools for Python codebases.
pytest Debugging Flags
| Flag | Purpose | When to Use |
|---|---|---|
-x |
Stop on first failure | Isolating the first broken test |
--tb=long |
Full traceback | Need complete call stack |
--tb=short |
Abbreviated traceback | Quick scan of failures |
--pdb |
Drop into debugger on failure | Interactive investigation |
--lf |
Re-run only last failed tests | Iterating on a fix |
-s |
Disable output capture | See print/logging output |
-k "pattern" |
Run tests matching pattern | Focus on specific tests |
-v |
Verbose output | See individual test names |
--no-header |
Suppress header | Cleaner output for parsing |
--tb=no |
No traceback | Quick pass/fail check |
Combining Flags
# Reproduce and investigate: stop at first failure, full trace, show output
pytest -x --tb=long -s path/to/test_file.py
# Re-run failures with debugger
pytest --lf --pdb
# Run specific test with verbose output
pytest -xvs -k "test_specific_function" path/to/test_file.py
Traceback Analysis
Reading Python Tracebacks
Python tracebacks read bottom-to-top — the actual error is at the bottom, the call chain above.
Traceback (most recent call last): ← oldest frame
File "app/main.py", line 42, in handle ← your code (relevant)
result = service.process(data)
File "app/service.py", line 18, in process ← your code (relevant)
return transformer.apply(data)
File "venv/lib/.../transformer.py", line 7 ← library code (context)
raise ValueError("invalid input")
ValueError: invalid input ← actual error (start here)
Frame Identification
| Frame Pattern | Type | Action |
|---|---|---|
Your project paths (app/, src/, lib/) |
Your code | Primary investigation target |
venv/, site-packages/ |
Library code | Check what you passed in |
<frozen importlib>, <string> |
Python internals | Usually skip |
Key Questions at Each Frame
- What arguments were passed to this function?
- What state was the object in at this point?
- Is the data type what the function expects?
Common Exception Types
| Exception | Typical Cause | Investigation Strategy |
|---|---|---|
AttributeError |
Wrong type, None where object expected, typo | Check the object's actual type with type(), trace where it was assigned |
KeyError |
Missing dict key, wrong key name | Print available keys, check data source |
TypeError |
Wrong argument types, wrong number of args | Compare function signature with call site |
ImportError / ModuleNotFoundError |
Missing package, circular import, wrong path | Check sys.path, verify package installed, check for circular deps |
ValueError |
Correct type but invalid value | Check input data, trace where value originates |
IndexError |
List/tuple index out of range | Check collection length, off-by-one |
FileNotFoundError |
Wrong path, missing file | Print os.getcwd(), check relative vs absolute |
PermissionError |
File/directory permissions | Check file ownership and mode |
RecursionError |
Infinite recursion | Check base case, print recursion depth |
StopIteration |
Exhausted iterator, bare next() |
Use next(iter, default) or check before consuming |
UnicodeDecodeError |
Wrong encoding assumption | Check file encoding, use errors='replace' to diagnose |
Python Gotchas
Mutable Default Arguments
# BUG: shared list across all calls
def append_to(item, target=[]):
target.append(item)
return target
# FIX: use None sentinel
def append_to(item, target=None):
if target is None:
target = []
target.append(item)
return target
Late Binding Closures
# BUG: all functions return 4 (last value of i)
functions = [lambda: i for i in range(5)]
# FIX: capture with default argument
functions = [lambda i=i: i for i in range(5)]
Circular Imports
Symptoms: ImportError, AttributeError on module attribute, partially initialized module.
Investigation:
- Check import order — which module loads first?
- Move imports inside functions (lazy import) as a quick fix
- Restructure to break the cycle for a proper fix
None Comparisons
# BUG: can fail with objects that override __eq__
if value == None:
# FIX: use identity check
if value is None:
Integer Division
# Python 3: this is float division
result = 7 / 2 # 3.5
# Integer division requires //
result = 7 // 2 # 3
String Immutability and Identity
# BUG: 'is' checks identity, not equality
if name is "admin": # unreliable
# FIX: use == for value comparison
if name == "admin":
Diagnostic Logging
Targeted Debug Logging
import logging
logger = logging.getLogger(__name__)
# Temporary diagnostic (remove after fixing)
logger.debug("process_order called: order_id=%s, items=%d, total=%s",
order.id, len(order.items), order.total)
Quick Diagnostic Print
For rapid investigation (remove before committing):
# Print with context
print(f"DEBUG [{__name__}:{line}] variable={variable!r} type={type(variable)}")
# Print collection contents
print(f"DEBUG keys={list(data.keys())}")
print(f"DEBUG len={len(items)}, first={items[0] if items else 'EMPTY'}")
Logging Configuration for Debugging
# Enable debug logging for specific module
logging.getLogger("app.service").setLevel(logging.DEBUG)
# See all SQL queries (SQLAlchemy)
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)
Performance Investigation
cProfile Quick Start
# Profile a script
python -m cProfile -s cumulative script.py
# Profile a specific function in tests
python -m cProfile -s tottime -m pytest -x test_slow.py
Targeted Profiling
import cProfile
import pstats
profiler = cProfile.Profile()
profiler.enable()
# ... code to profile ...
profiler.disable()
stats = pstats.Stats(profiler)
stats.sort_stats('cumulative')
stats.print_stats(20) # top 20 functions
Git Bisect with pytest
# Start bisect
git bisect start
# Mark current (broken) as bad
git bisect bad
# Mark known good commit
git bisect good <commit-hash>
# Automate with pytest
git bisect run pytest -x path/to/failing_test.py
# When done
git bisect reset
Environment Investigation
# Check Python version
python --version
# Check installed package version
pip show <package-name>
# List all installed packages
pip list
# Check for conflicting dependencies
pip check
# Verify virtual environment is active
which python
echo $VIRTUAL_ENV
TypeScript/JavaScript Debugging Reference
Language-specific debugging techniques, common pitfalls, and diagnostic tools for TypeScript and JavaScript codebases.
Test Runner Debugging Flags
Jest
| Flag | Purpose | When to Use |
|---|---|---|
--verbose |
Show individual test results | See which specific tests pass/fail |
--bail |
Stop on first failure | Isolating the first broken test |
--detectOpenHandles |
Detect open async handles | Tests hang or don't exit |
--runInBand |
Run serially (no workers) | Debugging race conditions between tests |
--testPathPattern="pattern" |
Run matching test files | Focus on specific test file |
-t "pattern" |
Run matching test names | Focus on specific test case |
--no-cache |
Disable transform cache | Stale cache causing issues |
--forceExit |
Force exit after tests | Tests hang due to open handles |
--testTimeout=10000 |
Set timeout (ms) | Async tests timing out |
Vitest
| Flag | Purpose | When to Use |
|---|---|---|
--reporter=verbose |
Detailed output | See individual test results |
--bail 1 |
Stop on first failure | Isolating failures |
--run |
Run once (no watch) | CI or single-run debugging |
--testNamePattern="pattern" |
Run matching tests | Focus on specific test |
--no-threads |
Disable worker threads | Debugging thread-related issues |
--inspect |
Enable Node inspector | Interactive debugging |
Combining Flags
# Reproduce and investigate: stop at first failure, verbose, serial
npx jest --bail --verbose --runInBand path/to/test.spec.ts
# Re-run with open handle detection
npx jest --bail --detectOpenHandles --forceExit
# Vitest single test with inspection
npx vitest run --bail 1 --reporter=verbose path/to/test.spec.ts
Async/Await Pitfalls
Missing await
// BUG: test passes even when assertion should fail — promise not awaited
it('fetches data', () => {
const result = fetchData(); // missing await
expect(result).toBeDefined(); // tests the Promise object, not the result
});
// FIX: await the async operation
it('fetches data', async () => {
const result = await fetchData();
expect(result).toBeDefined();
});
Unhandled Promise Rejections
// BUG: error swallowed silently
async function process() {
riskyOperation(); // missing await — rejection goes unhandled
}
// FIX: await and handle
async function process() {
await riskyOperation(); // now rejection propagates properly
}
Promise.all Error Handling
// BUG: first rejection cancels everything, others may leak
try {
await Promise.all([taskA(), taskB(), taskC()]);
} catch (e) {
// only catches the first rejection
}
// FIX: use Promise.allSettled when you need all results
const results = await Promise.allSettled([taskA(), taskB(), taskC()]);
const failures = results.filter(r => r.status === 'rejected');
Timer-Based Tests
// BUG: real timers make tests slow and flaky
it('debounces input', async () => {
await new Promise(r => setTimeout(r, 500)); // real delay
});
// FIX: use fake timers
it('debounces input', () => {
jest.useFakeTimers();
triggerInput();
jest.advanceTimersByTime(500);
expect(handler).toHaveBeenCalled();
jest.useRealTimers();
});
Common TypeScript/JavaScript Gotchas
this Binding
// BUG: 'this' is undefined in callback
class Handler {
name = "handler";
handle() {
console.log(this.name); // undefined when called as callback
}
}
const h = new Handler();
button.onClick(h.handle); // 'this' is lost
// FIX: arrow function or bind
class Handler {
name = "handler";
handle = () => { // arrow preserves 'this'
console.log(this.name);
}
}
Closure/Scope in Loops
// BUG: all callbacks reference the same variable
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100); // prints 5,5,5,5,5
}
// FIX: use let (block-scoped)
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100); // prints 0,1,2,3,4
}
Type Narrowing Gotchas
// BUG: typeof null === 'object'
function process(value: string | object | null) {
if (typeof value === 'object') {
value.toString(); // crashes if null
}
}
// FIX: check null explicitly first
function process(value: string | object | null) {
if (value === null) return;
if (typeof value === 'object') {
value.toString(); // safe
}
}
Equality Comparisons
// BUG: type coercion surprises
0 == "" // true
0 == false // true
"" == false // true
null == undefined // true
// FIX: always use strict equality
0 === "" // false
0 === false // false
Array/Object Reference vs Value
// BUG: arrays/objects compared by reference
[1, 2] === [1, 2] // false
{ a: 1 } === { a: 1 } // false
// FIX: deep comparison
JSON.stringify(a) === JSON.stringify(b) // quick but order-sensitive
// or use lodash.isEqual, util.isDeepStrictEqual
Optional Chaining and Nullish Coalescing
// BUG: || treats 0 and "" as falsy
const port = config.port || 3000; // 0 becomes 3000
// FIX: use ?? for null/undefined only
const port = config.port ?? 3000; // 0 stays 0
Stack Trace Analysis
Node.js Stack Traces
Error: Connection refused
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1141) ← Node internal
at Connection.connect (node_modules/pg/lib/connection.js:38) ← library
at Pool._connect (node_modules/pg/lib/pool.js:89) ← library
at db.query (src/database.ts:45) ← your code
at UserService.findById (src/services/user.ts:22) ← your code
at GET /api/users/:id (src/routes/users.ts:15) ← entry point
Read top-to-bottom (opposite of Python). Your code is typically in the middle frames.
Async Stack Traces
Node.js may lose async context in stack traces. Enable --async-stack-traces or use --enable-source-maps for TypeScript.
# Better async traces
node --async-stack-traces dist/app.js
# TypeScript source maps in traces
node --enable-source-maps dist/app.js
Console Debugging Patterns
Structured Output
// Object inspection
console.dir(complexObject, { depth: null, colors: true });
// Table format for arrays of objects
console.table(users.map(u => ({ id: u.id, name: u.name, role: u.role })));
// Call stack at this point
console.trace("reached here");
// Group related logs
console.group("Processing order #123");
console.log("Items:", order.items.length);
console.log("Total:", order.total);
console.groupEnd();
Conditional Debugging
// Only log when condition is true
console.assert(items.length > 0, "Items array is empty!", { items });
// Count how many times a code path is hit
console.count("cache-miss");
console.count("cache-hit");
Node.js Inspector
Starting the Inspector
# Inspect a script
node --inspect-brk dist/app.js
# Inspect tests (Jest)
node --inspect-brk node_modules/.bin/jest --runInBand
# Inspect tests (Vitest)
npx vitest --inspect --no-threads
Open chrome://inspect in Chrome to connect to the debugger.
Common Error Patterns
| Error | Typical Cause | Investigation Strategy |
|---|---|---|
TypeError: Cannot read properties of undefined |
Accessing property on undefined value | Trace back to where the variable was assigned, check for missing data |
TypeError: X is not a function |
Wrong type, missing import, wrong module export | Check imports, verify the export exists |
ReferenceError: X is not defined |
Typo, missing import, scope issue | Check spelling, verify import path |
SyntaxError: Unexpected token |
Malformed JSON, wrong file extension, missing transpilation | Check the file content at the indicated position |
ERR_MODULE_NOT_FOUND |
Wrong import path, missing package | Verify path, check node_modules |
ECONNREFUSED |
Service not running, wrong port | Check if the target service is up |
EADDRINUSE |
Port already in use | lsof -i :PORT to find the process |
MaxListenersExceededWarning |
Event listener leak | Track where listeners are added without removal |
JavaScript heap out of memory |
Memory leak, large data processing | Profile with --max-old-space-size, check for retained references |
ETIMEOUT / ESOCKETTIMEDOUT |
Network timeout, slow service | Check network, increase timeout, add retry |
TypeScript-Specific Investigation
# Check what TypeScript compiles to
npx tsc --noEmit --pretty # type check without emitting
# See generated JavaScript for a file
npx tsc --outDir /tmp/debug --sourceMap path/to/file.ts
# Check TypeScript version
npx tsc --version
# Verify tsconfig resolution
npx tsc --showConfig
General Debugging Reference
Language-agnostic debugging strategies, systematic investigation methods, and common bug categories.
Systematic Debugging Methods
Binary Search for Bugs
Narrow the problem space by half at each step:
- Identify the full code path from input to incorrect output
- Place a diagnostic check at the midpoint
- Determine which half contains the bug
- Repeat in the failing half until the exact location is found
Works for: data transformation pipelines, middleware chains, multi-step processes.
Git Bisect
Automate binary search through commit history:
git bisect start
git bisect bad # current commit is broken
git bisect good <known-good-sha> # this commit was working
# Automated: let a test command drive the search
git bisect run <test-command> # returns 0 = good, non-0 = bad
git bisect reset # when done
Best for: regressions where you know "it used to work."
Delta Debugging
Minimize the input that triggers the bug:
- Start with the full failing input
- Remove half the input — does it still fail?
- If yes, keep the smaller input and repeat
- If no, restore and try removing the other half
- Continue until you find the minimal failing case
Works for: large inputs, complex configurations, test case reduction.
Rubber Duck Debugging
Explain the problem out loud (or in writing), step by step:
- State what the code is supposed to do
- Walk through the actual execution, line by line
- At each step, explain what the state should be vs. what it is
- The discrepancy often reveals itself during the explanation
5 Whys
Drill past symptoms to root causes:
Bug: Users see a 500 error on checkout
Why? → The payment API call throws a timeout
Why? → The request takes >30 seconds
Why? → The order total calculation is O(n²)
Why? → It recalculates item prices for each item pair
Why? → The discount logic compares every item against every other item
Root cause: Quadratic discount calculation algorithm
Stop when the answer is something you can directly fix.
Reading Stack Traces
Universal Patterns
| Element | What It Tells You |
|---|---|
| Error type/name | Category of failure (null access, type mismatch, etc.) |
| Error message | Specific details about what went wrong |
| File path + line number | Where the error was thrown |
| Function/method name | What was executing when it failed |
| Frame ordering | The call chain that led to the error |
What Stack Traces CAN'T Tell You
- Why the wrong value got there (you need to trace backwards)
- When the state became corrupted (may have happened much earlier)
- Where in async code the real problem is (async gaps in traces)
- Whether a caught-and-rethrown error lost its original context
Investigation Strategy
- Read the error message first — it often contains the key clue
- Find your code in the trace (skip framework/library frames)
- Read the immediate caller — what arguments were passed?
- Check the state at that point — are variables what you expect?
- Trace backwards from the error to where the data originated
Bug Categories
Off-by-One Errors
Symptoms: Missing first/last element, array index out of bounds, fencepost errors.
Check for:
<vs<=in loop conditions- 0-based vs 1-based indexing confusion
- Inclusive vs exclusive range boundaries
- Empty collection edge case (length 0)
Null/Undefined/None Errors
Symptoms: Null reference exceptions, "undefined is not a function," AttributeError.
Check for:
- Uninitialized variables
- Missing return values (functions that implicitly return null/undefined)
- Optional fields accessed without guards
- API responses with unexpected null fields
- Database queries returning no results
Race Conditions
Symptoms: Intermittent failures, works in debugger but fails normally, order-dependent results.
Check for:
- Shared mutable state accessed concurrently
- Missing locks/synchronization
- Read-then-write without atomicity
- Callback ordering assumptions
- File system operations assuming sequential access
Resource Leaks
Symptoms: Slow degradation, eventual crashes, "too many open files," memory growth.
Check for:
- File handles not closed (missing
close()/with/using) - Database connections not returned to pool
- Event listeners added but never removed
- Timers/intervals not cleared
- Temporary files not cleaned up
State Corruption
Symptoms: Inconsistent data, works sometimes but not always, cascade of errors after a specific action.
Check for:
- Mutation of shared objects
- Missing deep copies (aliased references)
- Partial updates (crash between related writes)
- Cache invalidation issues
- Global/singleton state modified by multiple code paths
Diagnostic Logging Strategy
Targeted Logging
Log at decision points and data boundaries:
[ENTRY] function_name called with: key_arg=value
[BRANCH] taking path X because condition=value
[DATA] received from external: summary_of_data
[EXIT] function_name returning: summary_of_result
Logging Anti-Patterns
| Anti-Pattern | Problem | Better Approach |
|---|---|---|
| Logging everything | Noise hides signal | Log at boundaries and decision points |
| Logging sensitive data | Security risk | Redact or hash sensitive fields |
| Logging inside tight loops | Performance impact, massive output | Log summary after loop, or sample every Nth iteration |
| Logging without context | "Error occurred" is useless | Include function name, key parameters, state |
| Leaving debug logs in code | Clutters production output | Use conditional debug level, remove before commit |
Effective Diagnostic Pattern
- Before the suspected area: Log inputs and state
- At decision points: Log which branch was taken and why
- After the suspected area: Log outputs and state
- Compare: Are the inputs/outputs what you expect at each point?
Investigation Checklist
Before proposing a fix, verify you can answer:
Understanding the Bug
- Can you reproduce the bug reliably?
- What is the expected behavior vs actual behavior?
- When did this start happening? (regression or latent bug?)
- Does it happen always, sometimes, or in specific conditions?
Root Cause Identification
- Have you identified the specific line(s) causing the issue?
- Do you understand WHY those lines produce the wrong result?
- Is this the root cause, or a symptom of a deeper issue?
- Could this same root cause affect other code paths?
Fix Validation
- Does the fix address the root cause, not just the symptom?
- Could the fix introduce new bugs? (side effects, changed behavior)
- Are there existing tests that should have caught this?
- Does the fix handle all edge cases for this code path?
Broader Impact
- Are there similar patterns elsewhere that might have the same bug?
- Does the fix require updates to documentation or configuration?
- Could this be a regression? If so, what change introduced it?
- Is a new test needed to prevent this from recurring?
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?