Skip to content

Debugging Workflow (/debug-issue) — express

Stack flavor: express. ← Back to overview

[!TIP] Use when the user reports an error, 500 status code, or "something is broken."

Trigger Conditions

  • Keywords: "Fix", "Debug", "Error", "Broken", "Doesn't work", "500", "404"
  • Context: Any specific bug report

0. Early Context Gathering (DO THIS FIRST)

[!IMPORTANT] Before diving into code, gather critical context to avoid wasting time in the wrong area.

  • [ ] Ask for screenshot if UI-related — identifies the exact component/view
  • [ ] Ask "what works vs what doesn't" — if field A works but field B doesn't, this points to field-specific logic
  • [ ] Identify the exact view/component name — "Pipeline view" vs "Company Directory" vs "Opportunity modal"
  • [ ] Check browser console — ask if there are errors or logs visible

1. Discovery & Reproduction

  • [ ] Logs: Check backend console logs and browser console/network tab
  • [ ] Trace: Locate the exact file and line where the error originates
  • [ ] State: Identify the data state (inputs/DB state) that causes the crash

2. Root Cause Analysis

  • [ ] Validation: Is it a Zod validation error? Check backend/src/validation/
  • [ ] Permissions: Is it an auth issue? Check authenticate middleware and backend/src/middleware/auth.ts
  • [ ] Database: Is the Drizzle query correct? Check backend/src/routes/*.ts and backend/src/db/schema.ts
  • [ ] Error routing: Is next(error) being called? Check backend/src/middleware/errorHandler.ts
  • [ ] Logic: Is it a null/undefined or incorrect business logic?
  • [ ] Regression: Did a recent migration or refactor cause this?

3. Proposal & Fix

  • [ ] Plan: Explain the root cause to the user before fixing
  • [ ] Implementation: Apply the fix carefully
  • [ ] Cleanup: Remove all console.log debug statements added during discovery

4. Verification

  • [ ] Test: Verify the specific case that was failing
  • [ ] Side Effects: Ensure the fix doesn't break related functionality
  • [ ] Architecture: Run /architecture-review if the fix involved UI or API changes

Known Issue Patterns

Form Dirty State Not Triggering

Symptoms: "Save button stays disabled when editing field X, but works for field Y"

Common Causes (check in order): 1. Field-whitelist hooks: hooks/useOpportunityFormDirty.ts only checks fields explicitly listed in opportunityRecordFields. If a field isn't there, it won't trigger dirty state. 2. Shared object references: Initial state and current state point to the same object. Fix with structuredClone(). 3. Wrong component/view: User may be on a different screen than assumed.

Quick Debug:

console.log('Initial:', JSON.stringify(initialState?.fieldName));
console.log('Current:', JSON.stringify(currentState?.fieldName));
console.log('isDirty:', isDirty);

API / Backend Errors

  • Check backend/src/routes/*.ts for the endpoint logic
  • Check backend/src/db/schema.ts for column definitions
  • Verify all catch blocks use next(error), not manual res.status(500)
  • Check Supabase RLS policies if getting 403/401

S3 / File Upload Errors

  • Check backend/src/services/s3Service.ts for S3 operation logic
  • Check backend/src/routes/documentRoutes.ts for the upload route
  • Verify AWS credentials in .env (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_S3_BUCKET)
  • Check multer middleware configuration for file size/type limits

UI Component Not Updating

  • Check if state is being set with a new reference (not mutating existing object)
  • Check if useMemo/useCallback dependencies are correct
  • Check if the parent component is re-rendering

Race Conditions / Causal Order Failures

Symptoms: "Modals closing too fast", "Intermittent 500 errors", "Deleted items reappearing briefly", "Notifications missing"

Common Causes: 1. Unawaited backend tasks: Side effects (notifications, logs) in routes not awaited before res.json() 2. Eager frontend closure: Modals close on click instead of after await deleteMutation.mutateAsync() 3. Missing mutateAsync: Using mutate (fire-and-forget) instead of mutateAsync

Quick Debug: - Backend: verify Promise.allSettled() is used for concurrent side effects - Frontend: verify mutateAsync is awaited before closing modals - Check for sequence markers in console: [DELETE][1/3], [DELETE][2/3]