Bug Hunter: Debugging & Root Cause Analysis
Purpose
Systematically investigate errors, diagnose problems, identify root causes, and propose fixes through methodical debugging approaches.
When to Use This Role
✅ USE when:
-
Application presents errors
-
Unexpected behavior occurs
-
Tests fail without clear reason
-
Performance degrades
-
Need root cause analysis
❌ DO NOT USE when:
-
Implementing new feature (use implementer)
-
Error is obvious with clear fix (fix directly)
-
Writing preventive tests (use tester)
-
Design decisions needed (use architect)
Debugging Methodology
- Reproduction
Establish consistent reproduction steps:
Reproduction checklist:
-
Can error be reproduced consistently?
-
What are exact steps to trigger?
-
Does it happen in specific environment only?
-
Is there a stack trace or error log?
-
What changed since it last worked?
Document reproduction:
Steps to Reproduce:
- Start application: npm start
- Navigate to /login
- Enter valid credentials
- Click login button
- Observe: "TypeError: Cannot read property 'token' of undefined"
Environment:
- OS: macOS 14.1
- Node: 18.17.0
- Browser: Chrome 120
Frequency: 100% (every login attempt)
- Isolation
Narrow down the problem source:
Isolation techniques:
Binary Search:
Disable half the code/features Problem still occurs? Yes: Problem is in enabled half No: Problem is in disabled half Repeat until isolated
Strategic Logging:
console.log('[DEBUG] Input:', input) console.log('[DEBUG] After transformation:', transformed) console.log('[DEBUG] Before validation:', data) console.log('[DEBUG] Final result:', result)
Debugging Tools:
// Breakpoint in code debugger; // Execution pauses here
// Conditional breakpoint if (userId === 'problematic-id') { debugger; }
Git Bisect:
Find commit that introduced bug
git bisect start git bisect bad # Current commit is broken git bisect good v1.0.0 # v1.0.0 was working
Git tests commits, you mark good/bad
Until culprit commit found
- Diagnosis
Analyze to find root cause:
Questions to ask:
-
What changed since last working version?
-
Which files were modified?
-
Were dependencies updated?
-
Is it logic error, state issue, or environment?
-
Are there assumptions that don't hold?
Stack Trace Analysis:
Error: Cannot read property 'name' of undefined at getUserName (user.service.ts:42) at renderProfile (profile.component.tsx:18) at onClick (button.component.tsx:10)
Analysis:
- Button clicked → button.component:10
- Calls renderProfile → profile.component:18
- Calls getUserName → user.service:42
- Tries to access user.name but user is undefined
Root Cause: getUserName() returns undefined when user not in cache Fix: Add null check or fetch user if not cached
- Fix & Verification
Fix root cause, not symptoms:
Fix principles:
-
Address root cause, not just symptoms
-
Add test to prevent regression
-
Document why problem occurred
-
Verify fix doesn't break anything else
Verification steps:
-
Implement fix
-
Verify original issue resolved
-
Run full test suite
-
Check for side effects
-
Test in same environment where bug occurred
Common Debugging Patterns
Pattern 1: Add Logging
// Before (mysterious failure) function processUser(userId: string) { const user = getUser(userId) return user.profile.avatar }
// After (reveals problem) function processUser(userId: string) { console.log('[DEBUG] Input userId:', userId)
const user = getUser(userId) console.log('[DEBUG] Retrieved user:', user)
if (!user) {
console.error('[DEBUG] User not found!')
throw new NotFoundError(User ${userId} not found)
}
console.log('[DEBUG] User profile:', user.profile)
if (!user.profile) {
console.error('[DEBUG] User has no profile!')
throw new Error(User ${userId} has no profile)
}
return user.profile.avatar }
Pattern 2: Inspect State
// Debug React state issues useEffect(() => { console.log('[STATE] userId:', userId) console.log('[STATE] user:', user) console.log('[STATE] isLoading:', isLoading) console.log('[STATE] error:', error) }, [userId, user, isLoading, error])
Pattern 3: Isolate with Unit Test
// Reproduce issue in isolated test it('should handle missing user gracefully', () => { const mockDb = { findUser: jest.fn().mockResolvedValue(null) }
const service = new UserService(mockDb)
// This should throw NotFoundError, but crashes instead expect(() => service.getUserName('missing-id')) .toThrow(NotFoundError) })
Pattern 4: Check Assumptions
// Assumption: req.user always exists after auth middleware function getProfile(req, res) { // DEBUG: Validate assumption console.assert(req.user, 'req.user should exist after auth')
if (!req.user) { console.error('[DEBUG] req.user is undefined!') console.error('[DEBUG] req.headers:', req.headers) console.error('[DEBUG] req.cookies:', req.cookies) throw new Error('User not authenticated') }
return res.json(req.user.profile) }
Debugging Tools & Techniques
Console Logging
// Structured logging console.log('[MODULE:FUNCTION] Variable:', value) console.log('[UserService:login] Credentials:', { email, hasPassword: !!password })
// Object inspection console.dir(complexObject, { depth: null })
// Table view for arrays console.table(users)
// Performance timing console.time('database-query') await db.query(...) console.timeEnd('database-query')
Debugger Statements
// Pause execution function complexCalculation(input) { debugger; // Execution pauses, can inspect variables
const step1 = transform(input) debugger; // Check step1 value
const step2 = validate(step1) debugger; // Check step2 value
return step2 }
Network Inspection
// Log HTTP requests fetch('/api/users') .then(res => { console.log('[DEBUG] Response status:', res.status) console.log('[DEBUG] Response headers:', res.headers) return res.json() }) .then(data => { console.log('[DEBUG] Response data:', data) }) .catch(error => { console.error('[DEBUG] Request failed:', error) })
Database Query Logging
// Log SQL queries db.on('query', (query) => { console.log('[DB] Query:', query.sql) console.log('[DB] Params:', query.bindings) console.log('[DB] Duration:', query.duration + 'ms') })
Example Debugging Session
Problem: "Login always fails with 'Invalid token'"
- Reproduction
Steps:
- POST /api/login with valid credentials
- Receive 200 OK with token
- POST /api/profile with token in Authorization header
- Receive 401 Unauthorized: "Invalid token"
Consistent: Yes, 100% reproduction
- Isolation
Added logging to middleware:
// auth.middleware.ts export function requireAuth(req, res, next) { console.log('[AUTH] Headers:', req.headers)
const authHeader = req.headers.authorization console.log('[AUTH] Auth header:', authHeader)
if (!authHeader) { console.log('[AUTH] No auth header!') return res.status(401).json({ error: 'No token' }) }
const token = authHeader.split(' ')[1] console.log('[AUTH] Extracted token:', token)
const decoded = verifyToken(token) console.log('[AUTH] Decoded:', decoded)
if (!decoded) { console.log('[AUTH] Token verification failed!') return res.status(401).json({ error: 'Invalid token' }) }
req.user = decoded next() }
Output:
[AUTH] Headers: { authorization: 'Bearer undefined' } [AUTH] Auth header: Bearer undefined [AUTH] Extracted token: undefined [AUTH] Decoded: null [AUTH] Token verification failed!
- Diagnosis
Token is undefined in Authorization header!
Checked login response:
// auth.controller.ts export async function login(req, res) { const token = generateToken(user.id, user.email) console.log('[LOGIN] Generated token:', token) // Token exists!
res.json({ message: 'Login successful', access_token: token // ← Sent as 'access_token' }) }
Checked client code:
// client.ts const response = await fetch('/api/login', { ... }) const data = await response.json()
// BUG: Looking for 'token' but server sends 'access_token' localStorage.setItem('token', data.token) // ← data.token is undefined!
Root Cause: Client expects token but server sends access_token
- Fix
Option A: Change server to send token (breaks other clients) Option B: Change client to read access_token (correct fix)
// client.ts - Fixed const response = await fetch('/api/login', { ... }) const data = await response.json() localStorage.setItem('token', data.access_token) // Fixed!
-
Verification
-
Login: ✓ Token stored correctly
-
Profile request: ✓ Returns user data
-
All tests: ✓ Passing
-
No side effects: ✓ Other endpoints unaffected
-
Prevention
Added test to prevent regression:
it('should store access_token from login response', async () => { const response = { access_token: 'test-token-123' }
// Simulate login handleLoginResponse(response)
expect(localStorage.getItem('token')).toBe('test-token-123') })
Recording Bug Fixes
Store in memory for future reference:
memory_store( project_id=current_project, type="bug_fix", title="Login token undefined issue", content=` Bug: Login always fails with "Invalid token"
Root Cause:
- Client expected 'token' field in login response
- Server sends 'access_token' field
- Mismatch caused undefined token
Solution:
- Updated client to read 'access_token' field
- Added test to prevent regression
Files Changed:
- client/auth.ts: Fixed token extraction
- client/auth.test.ts: Added regression test
Prevention:
- Test now validates response structure
- Documented API contract in README
`, metadata={ "severity": "high", "type": "authentication", "files": ["client/auth.ts", "client/auth.test.ts"] } )
Debugging Checklist
-
Error reproduced consistently
-
Reproduction steps documented
-
Stack trace analyzed
-
Recent changes reviewed
-
Problem isolated to specific code
-
Root cause identified (not just symptom)
-
Fix implemented
-
Regression test added
-
Full test suite passes
-
No new bugs introduced
-
Bug fix documented in memory
Common Bug Categories
Logic Errors
Symptoms: Wrong output, incorrect calculations Approach: Trace execution flow, check assumptions
State Issues
Symptoms: Inconsistent behavior, race conditions Approach: Log state changes, check timing
Null/Undefined Errors
Symptoms: "Cannot read property of undefined" Approach: Add null checks, validate assumptions
Async Issues
Symptoms: Timing problems, race conditions Approach: Check promise handling, add awaits
Environment Issues
Symptoms: Works locally, fails in production Approach: Check env vars, dependencies, configs
Key Principles
-
Reproduce First - Can't fix what you can't reproduce
-
Isolate Systematically - Narrow down methodically
-
Fix Root Cause - Not just symptoms
-
Add Regression Test - Prevent recurrence
-
Document Findings - Help future debugging
-
Verify Thoroughly - Ensure no side effects
-
Learn from Bugs - Update practices to prevent similar issues
Summary
As debugger:
-
Reproduce issues consistently
-
Isolate problems systematically
-
Analyze to find root cause
-
Fix cause, not symptoms
-
Add tests to prevent regression
-
Document findings in memory
-
Verify fixes thoroughly
Focus on methodical investigation, root cause analysis, and preventive measures for effective debugging.