# Debugging Guide Common debugging strategies and troubleshooting patterns for Flyer Crawler. ## Table of Contents - [Quick Debugging Checklist](#quick-debugging-checklist) - [Container Issues](#container-issues) - [Database Issues](#database-issues) - [Test Failures](#test-failures) - [API Errors](#api-errors) - [Authentication Problems](#authentication-problems) - [Background Job Issues](#background-job-issues) - [SSL Certificate Issues](#ssl-certificate-issues) - [Frontend Issues](#frontend-issues) - [Performance Problems](#performance-problems) - [Debugging Tools](#debugging-tools) --- ## Quick Debugging Checklist When something breaks, check these first: 1. **Are containers running?** ```bash podman ps ``` 2. **Is the database accessible?** ```bash podman exec flyer-crawler-postgres pg_isready -U postgres ``` 3. **Are environment variables set?** ```bash # Check .env.local exists cat .env.local ``` 4. **Are there recent errors in logs?** ```bash # Application logs podman logs -f flyer-crawler-dev # PM2 logs (production) pm2 logs flyer-crawler-api ``` 5. **Is Redis accessible?** ```bash podman exec flyer-crawler-redis redis-cli ping ``` --- ## Container Issues ### Container Won't Start **Symptom**: `podman start` fails or container exits immediately **Debug**: ```bash # Check container status podman ps -a # View container logs podman logs flyer-crawler-postgres podman logs flyer-crawler-redis podman logs flyer-crawler-dev # Inspect container podman inspect flyer-crawler-dev ``` **Common Causes**: - Port already in use - Insufficient resources - Configuration error **Solutions**: ```bash # Check port usage netstat -an | findstr "5432" netstat -an | findstr "6379" # Remove and recreate container podman stop flyer-crawler-postgres podman rm flyer-crawler-postgres # ... recreate with podman run ... ``` ### "Unable to connect to Podman socket" **Symptom**: `Error: unable to connect to Podman socket` **Solution**: ```bash # Start Podman machine podman machine start # Verify it's running podman machine list ``` ### Port Already in Use **Symptom**: `Error: port 5432 is already allocated` **Solutions**: **Option 1**: Stop conflicting service ```bash # Find process using port netstat -ano | findstr "5432" # Stop the service or kill process ``` **Option 2**: Use different port ```bash # Run container on different host port podman run -d --name flyer-crawler-postgres -p 5433:5432 ... # Update .env.local DB_PORT=5433 ``` --- ## Database Issues ### Connection Refused **Symptom**: `Error: connect ECONNREFUSED 127.0.0.1:5432` **Debug**: ```bash # 1. Check if PostgreSQL container is running podman ps | grep postgres # 2. Check if PostgreSQL is ready podman exec flyer-crawler-postgres pg_isready -U postgres # 3. Test connection podman exec flyer-crawler-postgres psql -U postgres -d flyer_crawler_dev -c "SELECT 1;" ``` **Common Causes**: - Container not running - PostgreSQL still initializing - Wrong credentials in `.env.local` **Solutions**: ```bash # Start container podman start flyer-crawler-postgres # Wait for initialization (check logs) podman logs -f flyer-crawler-postgres # Verify credentials match .env.local cat .env.local | grep DB_ ``` ### Schema Out of Sync **Symptom**: Tests fail with missing column or table errors **Cause**: `master_schema_rollup.sql` not in sync with migrations **Solution**: ```bash # Reset database with current schema podman exec -i flyer-crawler-postgres psql -U postgres -d flyer_crawler_dev < sql/drop_tables.sql podman exec -i flyer-crawler-postgres psql -U postgres -d flyer_crawler_dev < sql/master_schema_rollup.sql # Verify schema podman exec flyer-crawler-postgres psql -U postgres -d flyer_crawler_dev -c "\dt" ``` ### Query Performance Issues **Debug**: ```sql -- Enable query logging ALTER DATABASE flyer_crawler_dev SET log_statement = 'all'; -- Check slow queries SELECT query, mean_exec_time, calls FROM pg_stat_statements WHERE mean_exec_time > 100 ORDER BY mean_exec_time DESC LIMIT 10; -- Analyze query plan EXPLAIN ANALYZE SELECT * FROM flyers WHERE store_id = 1; ``` **Solutions**: - Add missing indexes - Optimize WHERE clauses - Use connection pooling - See [ADR-034](../adr/0034-repository-layer-method-naming-conventions.md) --- ## Test Failures ### Tests Pass on Windows, Fail in Container **Cause**: Platform-specific behavior (ADR-014) **Rule**: Container results are authoritative. Windows results are unreliable. **Solution**: ```bash # Always run tests in container podman exec -it flyer-crawler-dev npm test # For specific test podman exec -it flyer-crawler-dev npm test -- --run src/path/to/test.test.ts ``` ### Integration Tests Fail **Common Issues**: **1. Vitest globalSetup Context Isolation** **Symptom**: Mocks or spies don't work in integration tests **Cause**: `globalSetup` runs in separate Node.js context **Solutions**: - Mark test as `.todo()` and document limitation - Create test-only API endpoints - Use Redis-based mock flags See [CLAUDE.md#integration-test-issues](../../CLAUDE.md#integration-test-issues) for details. **2. Cache Stale After Direct SQL** **Symptom**: Test reads stale data after direct database insert **Cause**: Cache not invalidated **Solution**: ```typescript // After direct SQL insert await cacheService.invalidateFlyers(); ``` **3. Queue Interference** **Symptom**: Cleanup worker processes test data before assertions **Solution**: ```typescript const { cleanupQueue } = await import('../../services/queues.server'); await cleanupQueue.drain(); await cleanupQueue.pause(); // ... test ... await cleanupQueue.resume(); ``` ### Type Check Failures **Symptom**: `npm run type-check` fails **Debug**: ```bash # Run type check in container podman exec -it flyer-crawler-dev npm run type-check # Check specific file podman exec -it flyer-crawler-dev npx tsc --noEmit src/path/to/file.ts ``` **Common Causes**: - Missing type definitions - Incorrect imports - Type mismatch in function calls --- ## API Errors ### 404 Not Found **Debug**: ```bash # Check route registration grep -r "router.get" src/routes/ # Check route path matches request # Verify middleware order ``` **Common Causes**: - Route not registered in `server.ts` - Typo in route path - Middleware blocking request ### 500 Internal Server Error **Debug**: ```bash # Check application logs podman logs -f flyer-crawler-dev # Check Bugsink for errors # Visit: http://localhost:8443 (dev) or https://bugsink.projectium.com (prod) ``` **Common Causes**: - Unhandled exception - Database error - Missing environment variable **Solution Pattern**: ```typescript // Always wrap route handlers app.get('/api/endpoint', async (req, res) => { try { const result = await service.doSomething(); return sendSuccess(res, result); } catch (error) { return sendError(res, error); // Handles error types automatically } }); ``` ### 401 Unauthorized **Debug**: ```bash # Check JWT token in request # Verify token is valid and not expired # Test token decoding node -e "console.log(require('jsonwebtoken').decode('YOUR_TOKEN_HERE'))" ``` **Common Causes**: - Token expired - Invalid token format - Missing Authorization header - Wrong JWT_SECRET --- ## Authentication Problems ### OAuth Not Working **Debug**: ```bash # 1. Verify OAuth credentials cat .env.local | grep GOOGLE_CLIENT # 2. Check OAuth routes are registered grep -r "passport.authenticate" src/routes/ # 3. Verify redirect URI matches Google Console # Should be: http://localhost:3001/api/auth/google/callback ``` **Common Issues**: - Redirect URI mismatch in Google Console - OAuth not enabled (commented out in config) - Wrong client ID/secret See [AUTHENTICATION.md](../architecture/AUTHENTICATION.md) for setup. ### JWT Token Invalid **Debug**: ```typescript // Decode token to inspect import jwt from 'jsonwebtoken'; const decoded = jwt.decode(token); console.log('Token payload:', decoded); console.log('Expired:', decoded.exp < Date.now() / 1000); ``` **Solutions**: - Regenerate token - Check JWT_SECRET matches between environments - Verify token hasn't expired --- ## Background Job Issues ### Jobs Not Processing **Debug**: ```bash # Check if worker is running pm2 list # Check worker logs pm2 logs flyer-crawler-worker # Check Redis connection podman exec flyer-crawler-redis redis-cli ping # Check queue status node -e " const { flyerProcessingQueue } = require('./dist/services/queues.server.js'); flyerProcessingQueue.getJobCounts().then(console.log); " ``` **Common Causes**: - Worker not running - Redis connection lost - Queue paused - Job stuck in failed state **Solutions**: ```bash # Restart worker pm2 restart flyer-crawler-worker # Clear failed jobs node -e " const { flyerProcessingQueue } = require('./dist/services/queues.server.js'); flyerProcessingQueue.clean(0, 1000, 'failed'); " ``` ### Jobs Failing **Debug**: ```bash # Check failed jobs node -e " const { flyerProcessingQueue } = require('./dist/services/queues.server.js'); flyerProcessingQueue.getFailed().then(jobs => { jobs.forEach(job => console.log(job.failedReason)); }); " # Check worker logs for stack traces pm2 logs flyer-crawler-worker --lines 100 ``` **Common Causes**: - Gemini API errors - Database errors - Invalid job data --- ## SSL Certificate Issues ### Images Not Loading (ERR_CERT_AUTHORITY_INVALID) **Symptom**: Flyer images fail to load with `ERR_CERT_AUTHORITY_INVALID` in browser console **Cause**: Mixed hostname origins - user accesses via `localhost` but images use `127.0.0.1` (or vice versa) **Debug**: ```bash # Check which hostname images are using podman exec flyer-crawler-dev psql -U postgres -d flyer_crawler_dev \ -c "SELECT image_url FROM flyers LIMIT 1;" # Verify certificate includes both hostnames podman exec flyer-crawler-dev openssl x509 -in /app/certs/localhost.crt -text -noout | grep -A1 "Subject Alternative Name" # Check NGINX accepts both hostnames podman exec flyer-crawler-dev grep "server_name" /etc/nginx/sites-available/default ``` **Solution**: The dev container is configured to handle both hostnames: 1. Certificate includes SANs for `localhost`, `127.0.0.1`, and `::1` 2. NGINX `server_name` directive includes both `localhost` and `127.0.0.1` If you still see errors: ```bash # Rebuild container to regenerate certificate podman-compose down podman-compose build --no-cache flyer-crawler-dev podman-compose up -d ``` See [FLYER-URL-CONFIGURATION.md](../FLYER-URL-CONFIGURATION.md#ssl-certificate-configuration-dev-container) for full details. ### Self-Signed Certificate Not Trusted **Symptom**: Browser shows security warning for `https://localhost` **Temporary Workaround**: Accept the warning by clicking "Advanced" > "Proceed to localhost" **Permanent Fix (Recommended)**: Install the mkcert CA certificate to eliminate all SSL warnings. The CA certificate is located at `certs/mkcert-ca.crt` in the project root. See [`certs/README.md`](../../certs/README.md) for platform-specific installation instructions (Windows, macOS, Linux, Firefox). After installation: - Your browser will trust all mkcert certificates without warnings - Both `https://localhost/` and `https://127.0.0.1/` will work without SSL errors - Flyer images will load without `ERR_CERT_AUTHORITY_INVALID` errors ### NGINX SSL Configuration Test **Debug**: ```bash # Test NGINX configuration podman exec flyer-crawler-dev nginx -t # Check if NGINX is listening on 443 podman exec flyer-crawler-dev netstat -tlnp | grep 443 # Verify certificate files exist podman exec flyer-crawler-dev ls -la /app/certs/ ``` --- ## Frontend Issues ### Hot Reload Not Working **Debug**: ```bash # Check Vite is running curl http://localhost:5173 # Check for port conflicts netstat -an | findstr "5173" ``` **Solution**: ```bash # Restart dev server npm run dev ``` ### API Calls Failing (CORS) **Symptom**: `CORS policy: No 'Access-Control-Allow-Origin' header` **Debug**: ```typescript // Check CORS configuration in server.ts import cors from 'cors'; app.use( cors({ origin: env.FRONTEND_URL, // Should match http://localhost:5173 in dev credentials: true, }), ); ``` **Solution**: Verify `FRONTEND_URL` in `.env.local` matches the frontend URL --- ## Performance Problems ### Slow API Responses **Debug**: ```typescript // Add timing logs const start = Date.now(); const result = await slowOperation(); console.log(`Operation took ${Date.now() - start}ms`); ``` **Common Causes**: - N+1 query problem - Missing database indexes - Large payload size - No caching **Solutions**: - Use JOINs instead of multiple queries - Add indexes: `CREATE INDEX idx_name ON table(column);` - Implement pagination - Add Redis caching ### High Memory Usage **Debug**: ```bash # Check PM2 memory usage pm2 monit # Check container memory podman stats flyer-crawler-dev ``` **Common Causes**: - Memory leak - Large in-memory cache - Unbounded array growth --- ## Debugging Tools ### VS Code Debugger **Launch Configuration** (`.vscode/launch.json`): ```json { "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Debug Tests", "program": "${workspaceFolder}/node_modules/vitest/vitest.mjs", "args": ["--run", "${file}"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" } ] } ``` ### Logging ```typescript import { logger } from './utils/logger'; // Structured logging logger.info('Processing flyer', { flyerId, userId }); logger.error('Failed to process', { error, context }); logger.debug('Cache hit', { key, ttl }); ``` ### Database Query Logging ```typescript // In development, log all queries if (env.NODE_ENV === 'development') { pool.on('connect', () => { console.log('Database connected'); }); // Log slow queries const originalQuery = pool.query.bind(pool); pool.query = async (...args) => { const start = Date.now(); const result = await originalQuery(...args); const duration = Date.now() - start; if (duration > 100) { console.log(`Slow query (${duration}ms):`, args[0]); } return result; }; } ``` ### Redis Debugging ```bash # Monitor Redis commands podman exec -it flyer-crawler-redis redis-cli monitor # Check keys podman exec flyer-crawler-redis redis-cli keys "*" # Get key value podman exec flyer-crawler-redis redis-cli get "flyer:123" # Check cache stats podman exec flyer-crawler-redis redis-cli info stats ``` --- ## See Also - [TESTING.md](TESTING.md) - Testing strategies - [CODE-PATTERNS.md](CODE-PATTERNS.md) - Common patterns - [MONITORING.md](../operations/MONITORING.md) - Production monitoring - [Bugsink Setup](../tools/BUGSINK-SETUP.md) - Error tracking - [DevOps Guide](../subagents/DEVOPS-GUIDE.md) - Container debugging