API Error Handling Issues Tracker
Created: 2025-08-25
Status: In Progress
Last Updated: 2025-08-25
Overview
Comprehensive review of error handling across all API routes in the web application. Issues are categorized by severity with specific file locations, problem descriptions, and proposed solutions.
🔴 CRITICAL Issues (2)
1. Google Drive Token Validation Missing Error Handling
File: /api/google-drive/status/route.ts
Lines: 113-125
Status: ⏳ Pending Review
Problem:
validateFolderAccesscall is not wrapped in try-catch- If folder validation throws an exception, the entire status check could crash
- No graceful degradation if Google Drive API is temporarily unavailable
Current Code Issue:
const folderValidation = await validateFolderAccess(accessToken, profile.google_drive_folder_id)
// No error handling if validateFolderAccess throwsImpact:
- Status endpoint could return 500 instead of degraded status
- Users can't see partial status information
- Critical for Google Drive integration reliability
Proposed Solution:
let folderValidation = { valid: false, error: 'Unable to validate' };
try {
folderValidation = await validateFolderAccess(accessToken, profile.google_drive_folder_id);
} catch (error) {
errorLog('GoogleDriveStatus', 'Folder validation failed', error);
// Continue with degraded status instead of crashing
}Decision: [ ] Required [ ] Not Required [ ] Needs Discussion
2. Database Transaction Rollbacks Missing in Admin Create Free User
File: /api/admin/create-free-user/route.ts
Lines: 93-102, 115
Status: ⏳ Pending Review
Problem:
- Stripe subscription created successfully (line 83-92)
- If database update fails (line 105-114), Stripe subscription remains but user profile not updated
- Partial rollback exists for Stripe failures but not for database failures after Stripe success
Current Code Issue:
// Stripe subscription created
stripeSubscription = await stripe.subscriptions.create({...});
// Database update - if this fails, Stripe subscription orphaned
const { error: profileError } = await supabaseAdmin
.from('user_profiles')
.update({...})
.eq('id', userId);
if (profileError) {
errorLog('AdminCreateFreeUser', 'Error updating user profile', profileError);
// Don't fail the whole operation, user is created
// PROBLEM: Stripe subscription exists but not linked to user
}Impact:
- User has Stripe subscription but app doesn't recognize it
- Manual intervention required to link subscription
- Potential billing issues
Proposed Solution:
if (profileError) {
errorLog('AdminCreateFreeUser', 'CRITICAL: Failed to link subscription to user', {
userId,
customerId: stripeCustomer.id,
subscriptionId: stripeSubscription.id,
error: profileError
});
// Attempt to cancel the subscription since we can't track it
try {
await stripe.subscriptions.cancel(stripeSubscription.id);
await stripe.customers.del(stripeCustomer.id);
} catch (rollbackError) {
errorLog('AdminCreateFreeUser', 'Failed to rollback Stripe resources', rollbackError);
}
return NextResponse.json(
{ error: 'Failed to complete user setup. Please try again.' },
{ status: 500 }
);
}Decision: [ ] Required [ ] Not Required [ ] Needs Discussion
🟠 HIGH Issues (3)
3. Profile Fetch Failures Silently Ignored in Job Analysis
File: /api/jobs/analyze/route.ts
Lines: 220-236
Status: ⏳ Pending Review
Problem:
- Profile/resume fetch wrapped in try-catch but errors not logged
- Analysis continues without resume data
- User not informed that analysis is incomplete
Current Code Issue:
try {
const { data: userProfile } = await supabase
.from('user_profiles')
.select('current_resume_json')
.eq('id', user.id)
.single();
if (userProfile?.current_resume_json) {
userResumeContent = userProfile.current_resume_json;
}
} catch (error) {
// Silently continues without resume data
// No error logging or user notification
}Impact:
- Job analysis quality degraded without user awareness
- Debugging issues difficult without error logs
- User might think full analysis was performed
Proposed Solution:
} catch (error) {
errorLog('JobAnalysis', 'Failed to fetch user resume for analysis', error);
// Include warning in response
analysisMetadata.warnings = ['Analysis performed without resume data'];
}Decision: [ ] Required [ ] Not Required [ ] Needs Discussion
4. Google Drive Folder Creation Without Cleanup
Files:
/api/jobs/[id]/cover-letter/route.ts(lines 320-325)/api/jobs/[id]/interview-prep/route.ts(lines 310-315)
Status: ⏳ Pending Review
Problem:
- Folder creation failures return 500 but don't clean up partial state
- Job records may have invalid folder references
- Subsequent document generation attempts fail
Current Code Issue:
const folder = await createJobFolder(job, accessToken);
if (!folder) {
return NextResponse.json(
{ error: 'Failed to create folder in Google Drive' },
{ status: 500 }
);
// No cleanup of job record or retry logic
}Impact:
- Job records corrupted with invalid folder IDs
- Users can't generate documents for those jobs
- Manual database cleanup required
Proposed Solution:
const folder = await createJobFolder(job, accessToken);
if (!folder) {
// Clear the invalid folder reference
await supabase
.from('saved_jobs')
.update({ google_drive_folder_id: null })
.eq('id', jobId);
return NextResponse.json(
{ error: 'Failed to create folder. Please try again.' },
{ status: 500 }
);
}Decision: [ ] Required [ ] Not Required [ ] Needs Discussion
5. Stripe Session Creation Not Wrapped in Try-Catch
File: /api/stripe/checkout/route.ts
Lines: 77-99
Status: ⏳ Pending Review
Problem:
- Direct Stripe API call without error wrapping
- Could expose sensitive Stripe error details to client
- No sanitization of error messages
Current Code Issue:
// Create Stripe Checkout session
const session = await stripe.checkout.sessions.create({
customer: customerId,
mode: 'subscription',
// ... configuration
});
// If this throws, raw Stripe error goes to clientImpact:
- Security risk: Stripe internal errors exposed
- Poor error messages for users
- Difficult to debug without proper logging
Proposed Solution:
let session;
try {
session = await stripe.checkout.sessions.create({...});
} catch (stripeError) {
errorLog('StripeCheckout', 'Failed to create checkout session', {
error: stripeError,
customerId,
priceId
});
return NextResponse.json(
{ error: 'Unable to create checkout session. Please try again.' },
{ status: 500 }
);
}Decision: [ ] Required [ ] Not Required [ ] Needs Discussion
🟡 MEDIUM Issues (3)
6. Database Query Error Logging Inconsistency
File: /api/usage/stats/route.ts
Lines: 76-80, 112-114
Status: ⏳ Pending Review
Problem:
- Some database errors have empty catch blocks
- Inconsistent error logging patterns
- Makes debugging production issues difficult
Current Code Issue:
if (summaryError) {
// Empty - no error logging
} else {
// Process success case only
}Impact:
- Silent failures in production
- Difficult to diagnose usage tracking issues
- Inconsistent with other routes
Proposed Solution:
if (summaryError) {
errorLog('UsageStats', 'Failed to fetch usage summary', summaryError);
// Continue with partial data or return error based on severity
}Decision: [ ] Required [ ] Not Required [ ] Needs Discussion
7. Email Sending Failures Don't Affect Response
File: /api/support/submit/route.tsx
Lines: 97-100, 111-114
Status: ⏳ Pending Review
Problem:
- Email failures are logged but response still shows success
- Users think support request failed when only email notification failed
- Support request is saved but team not notified
Current Code Issue:
try {
await sendEmail({...});
} catch (emailError) {
errorLog('Support', 'Failed to send email', emailError);
// Still returns success response
}
return NextResponse.json({
success: true,
message: 'Support request submitted'
});Impact:
- User confusion about request status
- Support team might miss urgent requests
- False success indication
Proposed Solution:
let emailSent = true;
try {
await sendEmail({...});
} catch (emailError) {
errorLog('Support', 'Failed to send email notification', emailError);
emailSent = false;
}
return NextResponse.json({
success: true,
message: emailSent
? 'Support request submitted successfully'
: 'Request saved but notification failed. We will still review your request.',
emailSent
});Decision: [ ] Required [ ] Not Required [ ] Needs Discussion
8. Rate Limiting Missing Retry Information
File: /api/auth/resend-verification/route.ts
Lines: 55-78
Status: ⏳ Pending Review
Problem:
- Rate limit errors don't include retry-after information
- Users don't know how long to wait
- Could lead to repeated failed attempts
Current Code Issue:
if (recentAttempts >= 3) {
return NextResponse.json(
{ error: 'Too many attempts. Please try again later.' },
{ status: 429 }
);
// Missing: When can they retry?
}Impact:
- Poor user experience
- Unnecessary retry attempts
- User frustration
Proposed Solution:
if (recentAttempts >= 3) {
const retryAfter = 3600; // 1 hour in seconds
return NextResponse.json(
{
error: `Too many attempts. Please try again in ${retryAfter / 60} minutes.`,
retryAfter
},
{
status: 429,
headers: { 'Retry-After': retryAfter.toString() }
}
);
}Decision: [ ] Required [ ] Not Required [ ] Needs Discussion
🟢 LOW Issues (2)
9. Generic Validation Error Messages
Multiple Files
Status: ⏳ Pending Review
Problem:
- Validation errors use generic messages
- Don't specify which field failed validation
- Makes client-side debugging harder
Example:
return NextResponse.json(
{ error: 'Invalid request data' },
{ status: 400 }
);Proposed Solution:
return NextResponse.json(
{
error: 'Validation failed',
fields: {
email: 'Invalid email format',
jobId: 'Job ID is required'
}
},
{ status: 400 }
);Decision: [ ] Required [ ] Not Required [ ] Needs Discussion
10. Inconsistent Success Response Format
Multiple Files
Status: ⏳ Pending Review
Problem:
- Some routes return
{ success: true, data: {...} } - Others return
{ result: {...} } - Others return data directly
Impact:
- Client needs multiple response handlers
- Inconsistent API design
- Harder to maintain
Proposed Solution: Standardize on:
// Success
{ success: true, data: {...}, message?: string }
// Error
{ success: false, error: string, code?: string }Decision: [ ] Required [ ] Not Required [ ] Needs Discussion
Summary Statistics
| Severity | Count | Reviewed | Fixed |
|---|---|---|---|
| 🔴 Critical | 2 | 2 | 0 |
| 🟠 High | 3 | 3 | 2 |
| 🟡 Medium | 3 | 1 | 1 |
| 🟢 Low | 2 | 0 | 0 |
| Total | 10 | 6 | 3 |
Fixed Issues
- ✅ Job Analysis Resume Fetch - Now fails fast with clear error message
- ✅ Google Drive Folder Cleanup - Both routes now clean up orphaned folders
- ✅ Usage Stats Error Logging - Now logs errors and warns users of partial data
Routes with Good Error Handling (Reference Examples)
- ✅
/api/account/delete/route.ts- Comprehensive error handling with cleanup - ✅
/api/admin/delete-user/route.ts- Good validation and permission checks - ✅
/api/google-drive/disconnect/route.ts- Graceful degradation - ✅
/api/jobs/save/route.ts- Proper usage limit checking
Next Steps
- Review each issue with stakeholder
- Mark decisions as Required/Not Required
- Implement fixes in order of severity
- Update this document as fixes are completed
