Use when errors occur deep in execution and you need to trace back to find the original trigger - systematically traces bugs backward through call stack to identify source of invalid data
Bugs often manifest deep in the call stack. Your instinct is to fix where the error appears, but that's treating a symptom.
Core principle: Trace backward through the call chain until you find the original trigger, then fix at the source.
Error: database query failed with null user_id
What code directly causes this?
await db.query('SELECT * FROM orders WHERE user_id = ?', [userId]);
OrderService.getOrders(userId)
→ called by OrderController.list()
→ called by router.get('/orders')
→ called by auth middleware
What value was passed?
userId = nullWhere did the problem originate?
// Auth middleware bug: didn't handle expired tokens
if (token.expired) {
// Missing: return error response
// Falls through with req.user = undefined
}
When you can't trace manually, add instrumentation:
async function getOrders(userId: string) {
const stack = new Error().stack;
console.error('DEBUG getOrders:', {
userId,
typeOfUserId: typeof userId,
stack,
});
// ... rest of function
}
Run and capture:
npm test 2>&1 | grep 'DEBUG getOrders'
Symptom: .git created in source directory during tests
Trace chain:
git init runs in process.cwd() ← empty cwd parametercontext.tempDir before beforeEach{ tempDir: '' } initiallyRoot cause: Top-level variable initialization accessing empty value
Fix: Made tempDir a getter that throws if accessed too early
Found immediate cause
↓
Can trace one level up? → YES → Trace backwards
↓ ↓
NO Is this the source?
↓ ↓
NEVER fix just YES → Fix at source
the symptom ↓
Add validation at each layer
↓
Bug impossible
NEVER fix just where the error appears. Trace back to find the original trigger.
console.error() not logger - logger may be suppressednew Error().stack shows complete call chainUsed by:
Pairs with: