Fullstack Debugger
Expert debugger for modern web stacks: Next.js 15, Cloudflare Workers, Supabase, and edge deployments. Systematic, evidence-based troubleshooting.
Activation Triggers
Activate on: "debug", "not working", "broken", "error", "500 error", "401", "403", "cache issue", "CORS error", "RLS policy", "auth not working", "blank page", "hydration error", "build failed", "worker not responding"
NOT for: Feature development → language skills | Architecture → system-architect | Performance optimization → performance-engineer
Debug Philosophy
- REPRODUCE → Can you make it fail consistently?
- ISOLATE → Which layer is broken?
- EVIDENCE → What do logs/network/state show?
- HYPOTHESIZE → What could cause this?
- TEST → Validate one hypothesis at a time
- FIX → Minimal change that resolves issue
- VERIFY → Confirm fix doesn't break other things
Architecture Layers
┌─────────────────────────────────────────────────────────────┐ │ DEBUGGING LAYERS │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Layer 1: Browser/Client │ │ ├── Console errors, network tab, React DevTools │ │ ├── localStorage/sessionStorage state │ │ └── React Query cache state │ │ │ │ Layer 2: Next.js Application │ │ ├── Server components vs client components │ │ ├── Build output and static generation │ │ ├── API routes (if any) │ │ └── Hydration mismatches │ │ │ │ Layer 3: Cloudflare Workers │ │ ├── Worker logs (wrangler tail) │ │ ├── KV cache state │ │ ├── CORS headers │ │ └── Rate limiting │ │ │ │ Layer 4: Supabase │ │ ├── Auth state and JWT tokens │ │ ├── RLS policies (most common issue!) │ │ ├── Database queries and indexes │ │ └── Realtime subscriptions │ │ │ │ Layer 5: External APIs │ │ ├── Third-party service availability │ │ ├── API rate limits │ │ └── Response format changes │ │ │ └─────────────────────────────────────────────────────────────┘
Quick Diagnosis Commands
Check Everything At Once
Run from next-app/ directory
echo "=== Build Check ===" && npm run build 2>&1 | tail -20 echo "=== TypeScript ===" && npx tsc --noEmit 2>&1 | head -20 echo "=== Lint ===" && npm run lint 2>&1 | head -20 echo "=== Git Status ===" && git status --short
Supabase RLS Diagnosis
Check if RLS is blocking queries (most common issue!)
node -e " const { createClient } = require('@supabase/supabase-js'); const supabase = createClient( 'YOUR_SUPABASE_URL', 'YOUR_ANON_KEY' );
async function diagnose() { // Test as anonymous user const { data, error, count } = await supabase .from('YOUR_TABLE') .select('*', { count: 'exact' }) .limit(5);
console.log('Error:', error); console.log('Count:', count); console.log('Sample:', data); } diagnose(); "
Worker Health Check
Check if workers are responding
curl -s -o /dev/null -w "%{http_code}" https://YOUR-WORKER.workers.dev/health
Check CORS headers
curl -s -D - -o /dev/null -H "Origin: https://yoursite.com"
https://YOUR-WORKER.workers.dev/api/endpoint | grep -iE "(access-control|x-)"
Stream worker logs
cd workers/your-worker && npx wrangler tail
Cache Inspection
Check Cloudflare KV cache
npx wrangler kv:key list --namespace-id=YOUR_NAMESPACE_ID | head -20
Get specific cached value
npx wrangler kv:key get --namespace-id=YOUR_NAMESPACE_ID "cache:key"
Clear a cached item
npx wrangler kv:key delete --namespace-id=YOUR_NAMESPACE_ID "cache:key"
Common Issues & Solutions
- RLS Policy Blocking Data (Most Common!)
Symptoms:
-
Query returns empty array but no error
-
Works in Supabase dashboard but not in app
-
Works for some users but not others
Diagnosis:
-- In Supabase SQL Editor -- Check what policies exist SELECT schemaname, tablename, policyname, permissive, roles, cmd, qual FROM pg_policies WHERE tablename = 'your_table';
-- Test as anonymous user SET ROLE anon; SELECT * FROM your_table LIMIT 5; RESET ROLE;
-- Test as authenticated user SET ROLE authenticated; SET request.jwt.claims = '{"sub": "user-uuid-here"}'; SELECT * FROM your_table LIMIT 5; RESET ROLE;
Common Fixes:
-- Allow public read access CREATE POLICY "Allow public read" ON your_table FOR SELECT USING (true);
-- Allow authenticated users to read CREATE POLICY "Allow authenticated read" ON your_table FOR SELECT TO authenticated USING (true);
-- Allow users to read their own data CREATE POLICY "Users read own data" ON your_table FOR SELECT USING (auth.uid() = user_id);
- CORS Errors
Symptoms:
-
"Access to fetch blocked by CORS policy"
-
Works in Postman but not in browser
-
Preflight request fails
Diagnosis:
Check what CORS headers are returned
curl -s -D - -o /dev/null
-H "Origin: https://yoursite.com"
-H "Access-Control-Request-Method: POST"
-X OPTIONS
https://your-worker.workers.dev/api/endpoint
Fix in Cloudflare Worker:
// In your worker const corsHeaders = { 'Access-Control-Allow-Origin': '*', // Or specific domain 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', };
// Handle preflight if (request.method === 'OPTIONS') { return new Response(null, { headers: corsHeaders }); }
// Add to all responses return new Response(data, { headers: { ...corsHeaders, 'Content-Type': 'application/json' } });
- Auth State Not Persisting
Symptoms:
-
User logged in but shows as logged out on refresh
-
Auth works locally but not in production
-
Session disappears randomly
Diagnosis:
// In browser console console.log('Session:', await supabase.auth.getSession()); console.log('User:', await supabase.auth.getUser()); console.log('LocalStorage:', Object.keys(localStorage).filter(k => k.includes('supabase')));
Common Fixes:
-
Check Supabase URL matches (http vs https, trailing slash)
-
Verify site URL in Supabase Auth settings
-
Check for cookie blocking (Safari, incognito)
-
Ensure AuthContext wraps all components needing auth
- Hydration Mismatch
Symptoms:
-
"Hydration failed because the initial UI does not match"
-
Content flashes on page load
-
Different content on server vs client
Diagnosis:
// Temporarily add to suspect component useEffect(() => { console.log('Client render:', document.body.innerHTML.slice(0, 500)); }, []);
Common Fixes:
// Use client-only rendering for dynamic content 'use client'; import { useState, useEffect } from 'react';
function DynamicContent() { const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); if (!mounted) return null; // or skeleton return <div>{/* dynamic content */}</div>; }
- Worker Not Deploying
Symptoms:
-
Deploy command succeeds but changes not reflected
-
Old code still running
-
Intermittent old/new behavior
Diagnosis:
Check deployment status
npx wrangler deployments list
View current worker code
npx wrangler deployments view
Check for multiple environments
npx wrangler whoami
Fixes:
Force redeploy
npx wrangler deploy --force
Clear Cloudflare cache
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache"
-H "Authorization: Bearer YOUR_TOKEN"
-H "Content-Type: application/json"
--data '{"purge_everything":true}'
- TypeScript Cache Haunting
Symptoms:
-
Errors reference deleted/changed code
-
Types don't match current code
-
"Cannot find module" for existing files
Fix:
Nuclear option - clear all caches
rm -rf .next node_modules/.cache tsconfig.tsbuildinfo npm run build
Or just TypeScript cache
rm -rf node_modules/.cache/typescript npx tsc --build --clean
- Static Export Issues
Symptoms:
-
"Error: Page X couldn't be rendered statically"
-
Dynamic routes fail in static export
-
API routes don't work after deploy
Diagnosis:
Check next.config for output mode
grep -A5 "output:" next.config.ts
Find dynamic components
grep -r "useSearchParams|usePathname|cookies()|headers()" src/
Fixes:
// For components using dynamic APIs export const dynamic = 'force-dynamic'; // or wrap in Suspense with fallback
// For generateStaticParams export async function generateStaticParams() { return [{ slug: 'page1' }, { slug: 'page2' }]; }
- Rate Limiting Issues
Symptoms:
-
429 errors after several requests
-
Works initially then stops
-
Different behavior per IP
Diagnosis:
Check rate limit headers
curl -i https://your-worker.workers.dev/api/endpoint 2>&1 | grep -i ratelimit
Check KV for rate limit keys
npx wrangler kv:key list --namespace-id=RATE_LIMIT_KV_ID | grep rate
Fixes:
Clear rate limit for an IP
npx wrangler kv:key delete --namespace-id=RATE_LIMIT_KV_ID "rate:192.168.1.1"
Adjust limits in wrangler.toml
RATE_LIMIT_REQUESTS = "100" RATE_LIMIT_WINDOW = "3600"
- Meeting/Location Data Issues
Symptoms:
-
No meetings found in certain areas
-
Stale meeting data
-
Cache showing wrong data
Diagnosis:
Check cache status for a location
curl -s -D - -o /dev/null
-H "Origin: https://yoursite.com"
"https://your-proxy.workers.dev/api/all?lat=45.52&lng=-122.68&radius=25"
| grep -iE "(x-cache|x-geohash|x-source)"
Force cache refresh
curl -H "Origin: https://yoursite.com"
"https://your-proxy.workers.dev/warm"
Check Supabase for meeting count
node -e " const { createClient } = require('@supabase/supabase-js'); const supabase = createClient('URL', 'KEY'); supabase.from('meetings').select('*', { count: 'exact', head: true }) .then(({count}) => console.log('Total meetings:', count)); "
- Build Fails on Cloudflare Pages
Symptoms:
-
Works locally but fails on deploy
-
"Module not found" errors
-
Memory exceeded
Diagnosis:
Check build output locally
NODE_ENV=production npm run build 2>&1 | tee build.log
Check for conditional imports
grep -r "require(" src/ --include=".ts" --include=".tsx"
Check bundle size
npx next-bundle-analyzer
Fixes:
// next.config.ts - increase memory module.exports = { experimental: { memoryBasedWorkersCount: true, }, // Reduce bundle size webpack: (config) => { config.externals = [...(config.externals || []), 'sharp']; return config; } };
Debug Scripts
scripts/diagnose.sh
#!/bin/bash
Run all diagnostics
echo "=== Environment ===" node -v && npm -v
echo "=== Dependencies ===" npm ls --depth=0 2>&1 | grep -E "(UNMET|missing)"
echo "=== TypeScript ===" npx tsc --noEmit 2>&1 | head -30
echo "=== Build ===" npm run build 2>&1 | tail -30
echo "=== Workers ===" for worker in workers/*/; do echo "Worker: $worker" (cd "$worker" && npx wrangler whoami 2>/dev/null) done
echo "=== Supabase ===" npx supabase status 2>/dev/null || echo "Supabase CLI not configured"
scripts/check-rls.js
// Check RLS policies are working correctly const { createClient } = require('@supabase/supabase-js');
const supabase = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY );
async function checkTable(table) {
console.log(\n=== Checking ${table} ===);
const { data, error, count } = await supabase
.from(table)
.select('*', { count: 'exact' })
.limit(1);
if (error) {
console.log(ERROR: ${error.message});
} else {
console.log(OK: ${count} rows accessible);
}
}
// Check critical tables ['profiles', 'meetings', 'forum_posts', 'journal_entries'].forEach(checkTable);
Validation Checklist
[ ] Can reproduce the issue consistently [ ] Identified which layer is failing (client/Next/Worker/Supabase/API) [ ] Checked browser console for errors [ ] Checked network tab for failed requests [ ] Checked worker logs (wrangler tail) [ ] Verified RLS policies allow access [ ] Tested with fresh browser/incognito [ ] Cleared all caches (browser, React Query, KV, TS) [ ] Checked environment variables match production [ ] Verified CORS headers are correct [ ] Tested on production URL (not just localhost) [ ] Created minimal reproduction case
Output
When debugging, always provide:
-
Root cause - What exactly was wrong
-
Evidence - Logs, errors, or queries that proved it
-
Fix - Minimal code change to resolve
-
Verification - How to confirm it's fixed
-
Prevention - How to avoid this in future
Tools Available
-
Read , Write , Edit
-
File operations
-
Bash
-
Run commands, curl, wrangler
-
Grep , Glob
-
Search codebase
-
WebFetch
-
Test endpoints
-
mcp__supabase__*
-
Direct Supabase operations
-
mcp__playwright__*
-
Browser automation for UI testing