Skip to main content

Error Handling

Scribe Backend uses standard HTTP status codes and provides detailed error messages to help you troubleshoot issues.

Error Response Format

All errors follow a consistent JSON structure:

Simple Error

{
  "detail": "Email not found or you don't have permission to access it"
}

Validation Error (422)

Pydantic validation errors include field-level details:
{
  "detail": [
    {
      "loc": ["body", "email_template"],
      "msg": "ensure this value has at least 10 characters",
      "type": "value_error.any_str.min_length"
    },
    {
      "loc": ["body", "recipient_name"],
      "msg": "field required",
      "type": "value_error.missing"
    }
  ]
}

Task Failure Error

Celery task failures include execution context:
{
  "task_id": "abc-123-def-456",
  "status": "FAILURE",
  "error": {
    "message": "Web scraping failed: timeout",
    "type": "ExternalAPIError",
    "failed_step": "web_scraper"
  }
}

HTTP Status Codes

2xx Success

CodeNameDescription
200OKRequest succeeded
201CreatedResource created successfully
202AcceptedAsync task accepted for processing

4xx Client Errors

CodeNameDescriptionCommon Causes
400Bad RequestInvalid request parametersInvalid UUID format, out-of-range values
401UnauthorizedMissing or invalid authenticationMissing Bearer token, expired JWT, invalid token
403ForbiddenAuthenticated but insufficient permissionsUser not initialized, accessing another user’s resource
404Not FoundResource not foundInvalid email_id, deleted queue item
422Unprocessable EntityValidation errorMissing required fields, invalid data types
429Too Many RequestsRate limit exceededSubmitting > 100 queue items

5xx Server Errors

CodeNameDescriptionAction
500Internal Server ErrorUnexpected server errorContact support with task_id
503Service UnavailableService temporarily unavailableRetry after delay, check /health endpoint

Common Error Scenarios

401 Unauthorized

Error:
{
  "detail": "Missing or invalid authorization header"
}
Solution:
curl -X GET https://scribeapi.manitmishra.com/api/user/profile \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
Error:
{
  "detail": "Invalid or expired token"
}
Solution:
  1. Refresh your token using Supabase refreshSession()
  2. If refresh fails, re-authenticate with Supabase
  3. Obtain a new JWT token

403 Forbidden

Error:
{
  "detail": "User not initialized. Call POST /api/user/init first."
}
Solution:
curl -X POST https://scribeapi.manitmishra.com/api/user/init \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"display_name": "Your Name"}'

404 Not Found

Error:
{
  "detail": "Email not found or you don't have permission to access it"
}
Possible Causes:
  • Invalid UUID format
  • Email belongs to another user
  • Email was deleted

422 Validation Error

Error:
{
  "detail": [
    {
      "loc": ["body", "recipient_name"],
      "msg": "field required",
      "type": "value_error.missing"
    }
  ]
}
Solution: Ensure all required fields are included:
{
  "email_template": "Hi {{name}}!",
  "recipient_name": "Dr. Jane Smith",
  "recipient_interest": "machine learning"
}
Error:
{
  "detail": [
    {
      "loc": ["body", "email_template"],
      "msg": "ensure this value has at least 10 characters",
      "type": "value_error.any_str.min_length"
    }
  ]
}
Constraints:
  • email_template: 10-5000 characters
  • recipient_name: 2-255 characters
  • recipient_interest: 2-500 characters

429 Rate Limit

Error:
{
  "detail": "Maximum 100 items per batch"
}
Solution: Split your batch into multiple requests with ≤100 items each.

500 Internal Server Error

Error:
{
  "detail": "Internal server error"
}
Action:
  1. Check the /health endpoint to verify service status
  2. Retry the request after a short delay
  3. If the issue persists, contact support with the task_id or request details

503 Service Unavailable

Error:
{
  "detail": "Authentication service unavailable"
}
Action:
  1. Check /health endpoint for database status
  2. Wait 30 seconds and retry
  3. If persistent, the service may be under maintenance

Task Execution Errors

When using async endpoints like POST /api/email/generate, task failures are reported via the status endpoint:

Pipeline Step Failures

{
  "task_id": "abc-123",
  "status": "FAILURE",
  "error": {
    "message": "ArXiv API timeout",
    "type": "ExternalAPIError",
    "failed_step": "arxiv_helper"
  }
}
Common Step Failures:
StepError TypeCommon Cause
template_parserValidationErrorInvalid template format
web_scraperExternalAPIErrorGoogle API quota, network timeout
arxiv_helperExternalAPIErrorArXiv API unavailable
email_composerValidationErrorLLM output parsing failed

Retrying Failed Tasks

Tasks with transient errors (API timeouts, rate limits) are automatically retried:
  • Max retries: 3
  • Retry delay: 30 seconds
  • Backoff: Exponential (30s, 60s, 120s)
After 3 failed retries, the task enters FAILURE state permanently. You must submit a new request to retry.

Error Recovery Strategies

Automatic Retry with Exponential Backoff

async function callAPIWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options)

      if (response.status === 429) {
        // Rate limited - wait and retry
        const delay = Math.pow(2, i) * 1000
        await new Promise(resolve => setTimeout(resolve, delay))
        continue
      }

      if (response.status >= 500) {
        // Server error - retry
        const delay = Math.pow(2, i) * 1000
        await new Promise(resolve => setTimeout(resolve, delay))
        continue
      }

      // Success or client error (don't retry)
      return response
    } catch (error) {
      // Network error - retry
      if (i === maxRetries - 1) throw error
      const delay = Math.pow(2, i) * 1000
      await new Promise(resolve => setTimeout(resolve, delay))
    }
  }
}

Handling Validation Errors

const response = await fetch('/api/email/generate', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(requestData)
})

if (response.status === 422) {
  const errors = await response.json()
  
  // Display field-level errors to user
  errors.detail.forEach(error => {
    const field = error.loc[error.loc.length - 1]
    console.error(`${field}: ${error.msg}`)
  })
}

Debugging Tips

Check /health endpoint

Verify service availability and database connectivity

Inspect task status

Use GET /api/email/status/{task_id} for detailed error info

Review Logfire traces

Access distributed traces with task_id for root cause analysis

Test in /docs

Use FastAPI’s interactive docs to isolate issues

Support

If you encounter persistent errors:
  1. Collect the following information:
    • HTTP status code
    • Error message
    • Request payload
    • task_id (if applicable)
    • Timestamp
  2. Check the /health endpoint for service status
  3. Review Common Error Scenarios above
  4. Contact support with the collected information

Authentication Guide

Troubleshoot authentication errors

Task Status

Monitor async task execution

Build docs developers (and LLMs) love