13 KiB
Troubleshooting Guide
Common issues and their solutions for ThrillWiki.
Authentication Issues
Cannot Sign In
Symptom: Sign in button does nothing or shows error
Possible Causes:
- Invalid credentials
- Email not verified
- Account banned/deactivated
- Supabase connection error
Solutions:
// Check browser console for errors
// Look for auth errors in console
// Verify Supabase connection
const { data, error } = await supabase.auth.getSession();
console.log('Session:', data, error);
// Check if email verified (if required)
SELECT email, email_confirmed_at FROM auth.users WHERE email = '[user-email]';
MFA Not Working
Symptom: MFA code not accepted or not prompted
Possible Causes:
- Time sync issue (TOTP codes are time-based)
- Wrong factor ID
- Challenge not created
- AAL level not checked
Solutions:
// Verify device time is correct (CRITICAL for TOTP)
// Check enrolled factors
const { data } = await supabase.auth.mfa.listFactors();
console.log('Enrolled factors:', data);
// Create challenge manually
const factor = data.totp[0];
const { data: challenge } = await supabase.auth.mfa.challenge({
factorId: factor.id
});
// Verify code
const { error } = await supabase.auth.mfa.verify({
factorId: factor.id,
challengeId: challenge.id,
code: '[6-digit-code]'
});
Session Expired
Symptom: User logged out unexpectedly
Possible Causes:
- Session timeout (default 1 hour)
- Token refresh failed
- Multiple tabs/windows
- AAL2 expired (MFA users)
Solutions:
// Check session status
const { data: { session } } = await supabase.auth.getSession();
if (!session) {
// Redirect to login
}
// Refresh session manually
const { data, error } = await supabase.auth.refreshSession();
// Monitor auth state changes
supabase.auth.onAuthStateChange((event, session) => {
console.log('Auth event:', event, session);
});
Submission Issues
Submission Not Appearing in Queue
Symptom: Created submission doesn't show in moderation queue
Possible Causes:
- Database write failed
- Status filter hiding submission
- RLS blocking view
- Realtime subscription not connected
Solutions:
-- Check if submission created
SELECT * FROM content_submissions
WHERE user_id = '[user-id]'
ORDER BY created_at DESC LIMIT 5;
-- Check submission items
SELECT * FROM submission_items
WHERE submission_id = '[submission-id]';
-- Check RLS policies
SET LOCAL ROLE authenticated;
SET LOCAL "request.jwt.claims" = '{"sub": "[moderator-user-id]"}';
SELECT * FROM content_submissions WHERE status = 'pending';
Cannot Approve Submission
Symptom: Approve button disabled or approval fails
Possible Causes:
- Lock expired
- No active lock
- Dependencies not approved
- Edge function error
Solutions:
// Check lock status
const { data: submission } = await supabase
.from('content_submissions')
.select('*, locked_until, assigned_to')
.eq('id', submissionId)
.single();
if (new Date(submission.locked_until) < new Date()) {
console.error('Lock expired');
}
// Check dependencies
const { data: items } = await supabase
.from('submission_items')
.select('*, depends_on')
.eq('submission_id', submissionId);
const blocked = items.filter(item =>
item.depends_on &&
items.find(dep => dep.id === item.depends_on && dep.status !== 'approved')
);
console.log('Blocked items:', blocked);
Image Upload Fails
Symptom: Image upload error or stuck at uploading
Possible Causes:
- File too large (>10MB)
- Invalid file type
- CloudFlare API error
- Network error
- CORS issue
Solutions:
// Check file size and type
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];
if (file.size > MAX_FILE_SIZE) {
throw new Error('File too large');
}
if (!ALLOWED_TYPES.includes(file.type)) {
throw new Error('Invalid file type');
}
// Check CloudFlare credentials
console.log('CF Account Hash:', import.meta.env.VITE_CLOUDFLARE_ACCOUNT_HASH);
// Test edge function
const { data, error } = await supabase.functions.invoke('upload-image', {
body: { action: 'get-upload-url' }
});
console.log('Upload URL response:', data, error);
// Check network tab for CORS errors
Moderation Queue Issues
Lock Stuck
Symptom: Submission locked but moderator not actively reviewing
Possible Causes:
- Moderator closed browser without releasing lock
- Lock extension failed
- Database lock not expired
Solutions:
-- Find stuck locks
SELECT
id,
submission_type,
assigned_to,
assigned_at,
locked_until,
EXTRACT(EPOCH FROM (NOW() - locked_until))/60 AS minutes_over
FROM content_submissions
WHERE locked_until IS NOT NULL
AND locked_until < NOW()
AND status = 'pending';
-- Release stuck lock (as admin)
UPDATE content_submissions
SET assigned_to = NULL,
assigned_at = NULL,
locked_until = NULL
WHERE id = '[submission-id]';
Realtime Not Working
Symptom: Queue doesn't update with new submissions
Possible Causes:
- Realtime subscription not connected
- RLS blocking realtime
- Filter mismatch
- Subscription channel wrong
Solutions:
// Check subscription status
const subscription = supabase
.channel('moderation_queue')
.on('postgres_changes', {
event: '*',
schema: 'public',
table: 'content_submissions',
filter: 'status=eq.pending'
}, payload => {
console.log('Realtime update:', payload);
})
.subscribe((status) => {
console.log('Subscription status:', status);
});
// Verify RLS allows realtime
SELECT * FROM content_submissions WHERE status = 'pending';
-- If this works, realtime should too
Versioning Issues
Version Not Created
Symptom: Entity updated but no version record
Possible Causes:
- Trigger not firing
- Session variables not set
- Trigger conditions not met
- Database error
Solutions:
-- Check if trigger exists
SELECT tgname, tgenabled
FROM pg_trigger
WHERE tgrelid = 'parks'::regclass;
-- Check session variables during update
SELECT
current_setting('app.current_user_id', true) AS user_id,
current_setting('app.submission_id', true) AS submission_id;
-- Manual version creation (as admin)
INSERT INTO park_versions (
park_id, version_number, created_by, change_type,
name, slug, description, -- ... all fields
)
SELECT
id,
COALESCE((SELECT MAX(version_number) FROM park_versions WHERE park_id = p.id), 0) + 1,
'[user-id]',
'updated',
name, slug, description -- ... all fields
FROM parks p
WHERE id = '[park-id]';
Version Comparison Fails
Symptom: Cannot compare versions or diff empty
Possible Causes:
- Version IDs invalid
- Function error
- No changes between versions
Solutions:
-- Verify versions exist
SELECT version_id, version_number, change_type
FROM park_versions
WHERE park_id = '[park-id]'
ORDER BY version_number DESC;
-- Test diff function
SELECT get_version_diff(
'park',
'[from-version-id]'::UUID,
'[to-version-id]'::UUID
);
-- If null, no changes detected
-- Check if fields actually different
SELECT
(SELECT name FROM park_versions WHERE version_id = '[from]') AS old_name,
(SELECT name FROM park_versions WHERE version_id = '[to]') AS new_name;
Performance Issues
Slow Queries
Symptom: Pages load slowly, timeouts
Possible Causes:
- Missing indexes
- N+1 queries
- Large dataset
- Complex joins
Solutions:
-- Find slow queries
SELECT
query,
calls,
total_exec_time,
mean_exec_time,
max_exec_time
FROM pg_stat_statements
WHERE mean_exec_time > 100
ORDER BY mean_exec_time DESC
LIMIT 20;
-- Add indexes
CREATE INDEX idx_rides_park_status ON rides(park_id, status);
CREATE INDEX idx_submissions_status_type ON content_submissions(status, submission_type);
-- Analyze query plan
EXPLAIN ANALYZE
SELECT * FROM rides WHERE park_id = '[park-id]' AND status = 'operating';
React Query Stale Data
Symptom: UI shows old data after update
Possible Causes:
- Cache not invalidated
- Stale time too long
- Background refetch disabled
Solutions:
// Invalidate specific queries after mutation
const mutation = useMutation({
mutationFn: updatePark,
onSuccess: () => {
queryClient.invalidateQueries(['parks', parkId]);
queryClient.invalidateQueries(['parks']); // List
}
});
// Force refetch
await queryClient.refetchQueries(['parks']);
// Disable cache for testing
const { data } = useQuery({
queryKey: ['parks'],
queryFn: fetchParks,
staleTime: 0,
gcTime: 0,
});
Build/Deploy Issues
Build Fails
Symptom: npm run build errors
Possible Causes:
- TypeScript errors
- Missing dependencies
- Environment variables missing
- Out of memory
Solutions:
# Type check first
npm run typecheck
# Clear cache
rm -rf node_modules dist .next
npm install
# Build with verbose logs
npm run build -- --verbose
# Increase memory limit
NODE_OPTIONS=--max_old_space_size=4096 npm run build
Edge Function Fails
Symptom: 500 error from edge function
Possible Causes:
- Secrets not set
- Syntax error
- Timeout
- Memory limit
Solutions:
# Test locally
supabase functions serve upload-image
# Check logs
supabase functions logs upload-image
# Verify secrets
supabase secrets list
# Set secrets
supabase secrets set CLOUDFLARE_API_TOKEN=[value]
Database Issues
RLS Blocking Query
Symptom: Query returns empty when data exists
Possible Causes:
- User not authenticated
- Wrong user role
- Policy conditions not met
- AAL level insufficient
Solutions:
-- Test as authenticated user
SET LOCAL ROLE authenticated;
SET LOCAL "request.jwt.claims" = '{"sub": "[user-id]", "role": "authenticated"}';
-- Check policies
SELECT * FROM pg_policies WHERE tablename = 'content_submissions';
-- Disable RLS temporarily (DEVELOPMENT ONLY)
ALTER TABLE content_submissions DISABLE ROW LEVEL SECURITY;
-- Remember to re-enable!
Migration Fails
Symptom: supabase db push fails
Possible Causes:
- Conflicting constraints
- Data violates new schema
- Circular dependencies
- Permission issues
Solutions:
-- Check for constraint violations
SELECT * FROM parks WHERE name IS NULL OR name = '';
-- Fix data before migration
UPDATE parks SET name = 'Unnamed Park' WHERE name IS NULL;
-- Drop constraints temporarily
ALTER TABLE parks DROP CONSTRAINT IF EXISTS parks_name_check;
-- Recreate after data fixed
ALTER TABLE parks ADD CONSTRAINT parks_name_check CHECK (name IS NOT NULL AND name != '');
User Reports
"I can't submit a park"
Checklist:
- Is user authenticated?
- Is user banned?
- Are all required fields filled?
- Is form validation passing?
- Check browser console for errors
"My submission disappeared"
Checklist:
- Check submission status in database
- Was it approved/rejected?
- Check notification logs
- Verify submission wasn't deleted
"I can't see the moderation queue"
Checklist:
- Is user a moderator?
- Is MFA completed (if enrolled)?
- Check RLS policies
- Verify user_roles table
Emergency Procedures
Database Locked Up
-- Find blocking queries
SELECT
blocked_locks.pid AS blocked_pid,
blocked_activity.usename AS blocked_user,
blocking_locks.pid AS blocking_pid,
blocking_activity.usename AS blocking_user,
blocked_activity.query AS blocked_query,
blocking_activity.query AS blocking_query
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;
-- Kill blocking query (CAREFUL!)
SELECT pg_terminate_backend([blocking_pid]);
Edge Function Infinite Loop
# Stop all executions
supabase functions delete [function-name]
# Fix and redeploy
supabase functions deploy [function-name]
Out of Database Connections
-- Check active connections
SELECT count(*) FROM pg_stat_activity;
-- Kill idle connections
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle'
AND state_change < NOW() - INTERVAL '5 minutes';
Getting Help
If stuck:
-
Check Documentation
- docs/ folder
- API documentation
-
Search Issues
- GitHub Issues
- Supabase Discord
- Stack Overflow
-
Ask for Help
- Create detailed issue
- Include error messages
- Provide reproduction steps
- Share relevant code/logs
-
Contact Support
- For critical production issues
- Email: [support email]
Last Updated: 2025-01-20