Skip to main content

Overview

All Repolyze API endpoints return consistent error responses in JSON format. This page documents all possible error scenarios, status codes, and error response formats.

Error Response Format

All errors follow this base structure:
interface ErrorResponse {
  error: string; // Human-readable error message
}

Enhanced Error Response (Daily Rate Limit)

Daily rate limit errors include additional fields:
interface DailyLimitErrorResponse {
  error: string;              // Error message with upgrade suggestion
  code: "DAILY_LIMIT_REACHED"; // Error code
  limit: number;              // User's daily limit
  remaining: number;          // Remaining analyses (always 0)
  tier: "anonymous" | "free" | "pro"; // User tier
}

HTTP Status Codes

CodeStatusDescriptionEndpoints
400Bad RequestInvalid request parameters or format/api/analyze, /api/branches
404Not FoundRepository or resource not found/api/analyze, /api/branches
413Payload Too LargeRequest body exceeds size limit/api/analyze
429Too Many RequestsRate limit exceededAll endpoints
500Internal Server ErrorUnexpected server errorAll endpoints
503Service UnavailableServer not properly configured/api/analyze

Error Details by Endpoint

POST /api/analyze

400 Bad Request

Invalid JSON:
{
  "error": "Invalid JSON in request body"
}
Invalid URL format:
{
  "error": "Invalid GitHub URL format. Expected: https://github.com/owner/repo"
}
Missing URL:
{
  "error": "URL is required"
}

404 Not Found

Repository not found:
{
  "error": "Repository not found or not accessible"
}
Branch not found:
{
  "error": "Branch 'feature-xyz' not found in vercel/next.js"
}

413 Payload Too Large

{
  "error": "Request body too large"
}
Trigger: Request body exceeds 10 KB (10,240 bytes)

429 Too Many Requests - Burst Limit

{
  "error": "Too many requests. Please try again later."
}
Headers:
Retry-After: 60
Details:
  • Limit: 10 requests per minute per IP
  • Window: 60 seconds rolling
  • Retry: Wait 60 seconds before retrying

429 Too Many Requests - Daily Limit (Anonymous)

{
  "error": "Daily limit reached. Sign in to get more analyses.",
  "code": "DAILY_LIMIT_REACHED",
  "limit": 1,
  "remaining": 0,
  "tier": "anonymous"
}
Details:
  • Limit: 1 analysis per day
  • Reset: 24 hours from first request
  • Solution: Sign in to increase limit to 3/day

429 Too Many Requests - Daily Limit (Free)

{
  "error": "Daily limit reached. Upgrade to Pro for 44 analyses per day.",
  "code": "DAILY_LIMIT_REACHED",
  "limit": 3,
  "remaining": 0,
  "tier": "free"
}
Details:
  • Limit: 3 analyses per day
  • Reset: 24 hours from first request
  • Solution: Upgrade to Pro for 44 analyses/day

429 Too Many Requests - Daily Limit (Pro)

{
  "error": "Daily analysis limit reached.",
  "code": "DAILY_LIMIT_REACHED",
  "limit": 44,
  "remaining": 0,
  "tier": "pro"
}
Details:
  • Limit: 44 analyses per day
  • Reset: 24 hours from first request

500 Internal Server Error

Generic error:
{
  "error": "Analysis failed. Please try again."
}
Operational errors (rate limit, not found, etc.) return specific error messages:
{
  "error": "GitHub API rate limit exceeded"
}

503 Service Unavailable

{
  "error": "Server is not properly configured."
}
Cause: Missing required environment variables (OPENROUTER_API_KEY)

POST /api/branches

400 Bad Request

Missing URL:
{
  "error": "URL is required"
}
Invalid URL format:
{
  "error": "Invalid GitHub URL format. Expected: https://github.com/owner/repo"
}

404 Not Found

{
  "error": "Repository not found or not accessible"
}

429 Too Many Requests

{
  "error": "Too many requests. Please try again later."
}
Headers:
Retry-After: 60

500 Internal Server Error

{
  "error": "Failed to fetch branches"
}

Streaming Errors

When using the /api/analyze streaming endpoint, errors can occur mid-stream:

Stream Error Event

interface StreamEventError {
  type: "error";
  data: string; // Error message
}
Example:
{"type":"error","data":"AI analysis failed: Model timeout"}
Common streaming errors:
Error MessageCauseSolution
"Repository not found"Invalid repo URL or private repoVerify URL and ensure repo is public
"Branch not found"Invalid branch nameUse /api/branches to get valid branches
"GitHub API rate limit exceeded"Too many GitHub API callsWait for rate limit reset
"AI analysis failed"OpenRouter API errorRetry the request
"Model timeout"AI model took too longRetry with smaller repository

Error Handling Best Practices

TypeScript Error Handler

interface ErrorResponse {
  error: string;
  code?: string;
  limit?: number;
  remaining?: number;
  tier?: string;
}

class RepolyzeError extends Error {
  constructor(
    message: string,
    public statusCode: number,
    public code?: string,
    public metadata?: Record<string, any>
  ) {
    super(message);
    this.name = 'RepolyzeError';
  }
}

async function analyzeWithErrorHandling(url: string, branch?: string) {
  try {
    const response = await fetch('https://repolyze.ossium.live/api/analyze', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ url, branch })
    });

    if (!response.ok) {
      const errorData: ErrorResponse = await response.json();
      
      throw new RepolyzeError(
        errorData.error,
        response.status,
        errorData.code,
        {
          limit: errorData.limit,
          remaining: errorData.remaining,
          tier: errorData.tier
        }
      );
    }

    return response;
  } catch (error) {
    if (error instanceof RepolyzeError) {
      switch (error.statusCode) {
        case 400:
          console.error('Invalid request:', error.message);
          break;
        case 404:
          console.error('Not found:', error.message);
          break;
        case 429:
          if (error.code === 'DAILY_LIMIT_REACHED') {
            console.error(`Daily limit reached (${error.metadata?.tier})`);
            console.error(`Limit: ${error.metadata?.limit}/day`);
          } else {
            console.error('Rate limited. Wait 60 seconds.');
          }
          break;
        case 500:
          console.error('Server error:', error.message);
          break;
        case 503:
          console.error('Service unavailable:', error.message);
          break;
        default:
          console.error('Unknown error:', error.message);
      }
    } else {
      console.error('Network error:', error);
    }
    
    throw error;
  }
}

React Error Boundary

import { Component, ReactNode } from 'react';

interface Props {
  children: ReactNode;
  fallback?: (error: Error) => ReactNode;
}

interface State {
  hasError: boolean;
  error: Error | null;
}

export class RepolyzeErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: any) {
    console.error('Repolyze error:', error, errorInfo);
  }

  render() {
    if (this.state.hasError && this.state.error) {
      if (this.props.fallback) {
        return this.props.fallback(this.state.error);
      }

      return (
        <div className="error-container">
          <h2>Analysis Error</h2>
          <p>{this.state.error.message}</p>
          <button onClick={() => this.setState({ hasError: false, error: null })}>
            Try Again
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

Retry with Exponential Backoff

async function analyzeWithRetry(
  url: string,
  branch?: string,
  maxRetries = 3,
  baseDelay = 1000
) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch('https://repolyze.ossium.live/api/analyze', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ url, branch })
      });

      if (response.ok) {
        return response;
      }

      // Don't retry on client errors (4xx except 429)
      if (response.status >= 400 && response.status < 500 && response.status !== 429) {
        const error = await response.json();
        throw new Error(error.error);
      }

      // Handle rate limit
      if (response.status === 429) {
        const retryAfter = response.headers.get('Retry-After');
        const delay = retryAfter ? parseInt(retryAfter) * 1000 : baseDelay * Math.pow(2, attempt);
        
        console.log(`Rate limited. Retrying after ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }

      // Retry on server errors (5xx)
      if (response.status >= 500) {
        const delay = baseDelay * Math.pow(2, attempt);
        console.log(`Server error. Retrying after ${delay}ms... (attempt ${attempt + 1}/${maxRetries})`);
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }

      const error = await response.json();
      throw new Error(error.error);
    } catch (error) {
      if (attempt === maxRetries - 1) {
        throw error;
      }
      
      const delay = baseDelay * Math.pow(2, attempt);
      console.log(`Request failed. Retrying after ${delay}ms... (attempt ${attempt + 1}/${maxRetries})`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }

  throw new Error('Max retries exceeded');
}

User-Friendly Error Messages

function getUserFriendlyMessage(error: RepolyzeError): string {
  switch (error.statusCode) {
    case 400:
      if (error.message.includes('Invalid GitHub URL')) {
        return 'Please enter a valid GitHub repository URL (e.g., https://github.com/owner/repo)';
      }
      return 'The request was invalid. Please check your input.';
      
    case 404:
      if (error.message.includes('Branch')) {
        return 'The specified branch does not exist. Please select a valid branch.';
      }
      return 'Repository not found. Please check the URL and ensure the repository is public.';
      
    case 413:
      return 'The request is too large. Please contact support.';
      
    case 429:
      if (error.code === 'DAILY_LIMIT_REACHED') {
        const tier = error.metadata?.tier;
        if (tier === 'anonymous') {
          return 'Daily limit reached. Sign in to analyze up to 3 repositories per day.';
        } else if (tier === 'free') {
          return 'Daily limit reached. Upgrade to Pro to analyze up to 44 repositories per day.';
        }
        return 'Daily analysis limit reached. Please try again tomorrow.';
      }
      return 'Too many requests. Please wait a moment and try again.';
      
    case 500:
      return 'Something went wrong on our end. Please try again in a moment.';
      
    case 503:
      return 'The service is temporarily unavailable. Please try again later.';
      
    default:
      return 'An unexpected error occurred. Please try again.';
  }
}

Validation Helpers

URL Validation

function validateGitHubUrl(url: string): { valid: boolean; error?: string } {
  if (!url) {
    return { valid: false, error: 'URL is required' };
  }

  const pattern = /^https:\/\/github\.com\/([\w.-]+)\/([\w.-]+)\/?$/;
  const match = url.match(pattern);

  if (!match) {
    return {
      valid: false,
      error: 'Invalid GitHub URL format. Expected: https://github.com/owner/repo'
    };
  }

  const [, owner, repo] = match;

  if (owner.length < 1 || owner.length > 39) {
    return { valid: false, error: 'Invalid owner name length' };
  }

  if (repo.length < 1 || repo.length > 100) {
    return { valid: false, error: 'Invalid repository name length' };
  }

  return { valid: true };
}

// Usage
const validation = validateGitHubUrl(userInput);
if (!validation.valid) {
  console.error(validation.error);
}

Branch Validation

async function validateBranch(
  url: string,
  branch: string
): Promise<{ valid: boolean; error?: string }> {
  try {
    const response = await fetch('/api/branches', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ url })
    });

    if (!response.ok) {
      return { valid: false, error: 'Failed to fetch branches' };
    }

    const { branches } = await response.json();
    const branchExists = branches.some((b: any) => b.name === branch);

    if (!branchExists) {
      return {
        valid: false,
        error: `Branch "${branch}" not found. Available branches: ${branches.map((b: any) => b.name).join(', ')}`
      };
    }

    return { valid: true };
  } catch (error) {
    return { valid: false, error: 'Failed to validate branch' };
  }
}

Build docs developers (and LLMs) love