This skill should be used when the user reports "error", "bug", "not working", "failing tests", "unexpected behavior", "investigate issue", or describes something broken or malfunctioning. Use for...
Systematically investigate errors, diagnose problems, identify root causes, and propose fixes through methodical debugging approaches.
✅ USE when:
❌ DO NOT USE when:
Establish consistent reproduction steps:
Reproduction checklist:
Document reproduction:
Steps to Reproduce:
1. Start application: npm start
2. Navigate to /login
3. Enter valid credentials
4. Click login button
5. 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)
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
Analyze to find root cause:
Questions to ask:
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:
1. Button clicked → button.component:10
2. Calls renderProfile → profile.component:18
3. Calls getUserName → user.service:42
4. 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 root cause, not symptoms:
Fix principles:
Verification steps:
// 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
}
// 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])
// 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)
})
// 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)
}
// 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')
// 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
}
// 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)
})
// 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')
})
Problem: "Login always fails with 'Invalid token'"
Steps:
1. POST /api/login with valid credentials
2. Receive 200 OK with token
3. POST /api/profile with token in Authorization header
4. Receive 401 Unauthorized: "Invalid token"
Consistent: Yes, 100% reproduction
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!
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
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!
1. Login: ✓ Token stored correctly
2. Profile request: ✓ Returns user data
3. All tests: ✓ Passing
4. No side effects: ✓ Other endpoints unaffected
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')
})
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"]
}
)
Symptoms: Wrong output, incorrect calculations Approach: Trace execution flow, check assumptions
Symptoms: Inconsistent behavior, race conditions Approach: Log state changes, check timing
Symptoms: "Cannot read property of undefined" Approach: Add null checks, validate assumptions
Symptoms: Timing problems, race conditions Approach: Check promise handling, add awaits
Symptoms: Works locally, fails in production Approach: Check env vars, dependencies, configs
As debugger:
Focus on methodical investigation, root cause analysis, and preventive measures for effective debugging.