DEV Community

myougaTheAxo
myougaTheAxo

Posted on

5 Debugging Patterns with Claude Code: From Stack Trace to Root Cause

Dumping an error message into Claude Code and saying "fix it" works sometimes. But these five patterns consistently produce better results for real debugging sessions.


Pattern 1: Full Stack Trace + Relevant Code

Don't just paste the error message. Paste the full stack trace AND the code at the call sites:

This error is happening:

TypeError: Cannot read properties of undefined (reading 'email')
    at UserService.getUser (src/services/user.ts:24:30)
    at UserController.getProfile (src/controllers/user.ts:15:35)
    at Layer.handle [as handle_request] (express/lib/router/layer.js:95:5)

Here's the UserService code:
[paste the relevant code]

What's the root cause and how should I fix it?
Enter fullscreen mode Exit fullscreen mode

The stack trace tells Claude where the error is. The code gives it context to understand why.


Pattern 2: Ask for Hypotheses Before Fixes

When you're not sure what's wrong, ask for hypotheses before asking for a fix:

This function returns null when it shouldn't. Before fixing it,
give me 3 possible root causes:

Expected: user object with email field
Actual: null

[paste the code]
Enter fullscreen mode Exit fullscreen mode

This forces Claude Code to reason through the problem rather than jumping to the first plausible fix. You can then confirm which hypothesis matches before accepting the change.


Pattern 3: Ask for Logging Strategy First

When you can't reproduce the bug reliably, ask where to add logs:

This function is behaving unexpectedly in production but I can't
reproduce it locally. Where should I add logging to narrow down
the cause? Don't fix it yet — just tell me what to log and where.

[paste the code]
Enter fullscreen mode Exit fullscreen mode

This is often faster than guessing at fixes.


Pattern 4: Environment Diff Analysis

For "works in production, fails in development" bugs:

Same code, different behavior:

Production: Node.js 18, PostgreSQL 15 → works
Development: Node.js 20, PostgreSQL 16 → errors

Error:
[paste the error]

What environment differences could cause this? What should I check?
Enter fullscreen mode Exit fullscreen mode

Pattern 5: Regression Pinpointing

When something broke after a change but you're not sure what:

This was working before my last PR. The PR changed:
- Added rate limiting middleware
- Updated the user schema
- Changed auth token validation

Now I'm getting:
[paste the error]

Which change most likely caused this and why?
Enter fullscreen mode Exit fullscreen mode

CLAUDE.md: Document Your Common Error Patterns

Add a debugging section to your CLAUDE.md so Claude Code already knows your project's error patterns:

## Common Error Patterns

- `NullReferenceError` on user object: Usually means the Prisma query
  is missing `include: { profile: true }`. Check the repository layer.

- `ValidationError` from Pydantic: Check if the API schema matches
  the actual data shape. Schema files are in `src/schemas/`.

- `TimeoutError` on external calls: All external API calls need
  explicit timeout configuration. See `src/lib/http.ts` for the wrapper.

- `UNIQUE constraint failed`: Missing uniqueness check before insert.
  Use the UserRepository.existsByEmail() pattern.

## Debugging Tools
- Logs: `logs/app.log` (JSON format, use jq)
- DB inspection: `npm run db:studio`
- Request tracing: X-Request-ID header in all logs
Enter fullscreen mode Exit fullscreen mode

Hook: Auto-Log Failed Bash Commands

Add this hook to automatically record when Claude Code runs commands that fail:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{
          "type": "command",
          "command": "python .claude/hooks/log_failures.py"
        }]
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode
# .claude/hooks/log_failures.py
import json, sys
from pathlib import Path
from datetime import datetime

data = json.load(sys.stdin)
if data.get("tool_name") == "Bash":
    result = data.get("tool_result", {})
    if result.get("exit_code", 0) != 0:
        log = Path(".claude/debug.log")
        ts = datetime.now().strftime("%H:%M:%S")
        cmd = data.get("tool_input", {}).get("command", "")[:100]
        out = result.get("output", "")[:300]
        with log.open("a") as f:
            f.write(f"[{ts}] FAILED: {cmd}\nOutput: {out}\n\n")
sys.exit(0)
Enter fullscreen mode Exit fullscreen mode

This creates a .claude/debug.log that accumulates all failed commands during a session, useful for diagnosing what went wrong.


Quick Reference

Situation Prompt Pattern
Clear error Full stack trace + relevant code
Unclear cause Ask for 3 hypotheses first
Can't reproduce Ask for logging strategy
Env-specific bug Describe both envs, ask for diffs
Regression List what changed, ask which caused it

For automated pre-commit security and code quality checks, Security Pack and Code Review Pack (¥980 each) are available on PromptWorks.

👉 prompt-works.jp

Myouga (@myougatheaxo) — Security-focused Claude Code engineer.

Top comments (0)