LeapOCRLeapOCR Docs

Troubleshooting

Solutions to common issues and problems

Troubleshooting

Solutions to common issues when using LeapOCR.

Authentication Issues

Invalid API Key Error

Symptom: 401 Unauthorized or Authentication failed error

Solutions:

  1. Verify your API key is correct

    # Check that your key is properly set
    echo $LEAPOCR_API_KEY
  2. Check for whitespace

    // ❌ Bad - extra whitespace
    const client = new LeapOCR({ apiKey: " your-key " });
    
    // ✅ Good - trimmed
    const client = new LeapOCR({ apiKey: process.env.LEAPOCR_API_KEY.trim() });
  3. Verify key is active

    • Log into your dashboard
    • Check that the key hasn't been revoked
    • Regenerate if necessary

API Key Not Found

Symptom: undefined or empty API key

Solutions:

// ❌ Bad - might be undefined
const client = new LeapOCR({ apiKey: process.env.LEAPOCR_API_KEY });

// ✅ Good - validate first
const apiKey = process.env.LEAPOCR_API_KEY;
if (!apiKey) {
  throw new Error("LEAPOCR_API_KEY environment variable not set");
}
const client = new LeapOCR({ apiKey });

Processing Issues

Job Timeout

Symptom: Job takes too long and times out

Solutions:

  1. Increase timeout for large documents

    const result = await client.ocr.waitUntilDone(job.jobId, {
      timeout: 300000, // 5 minutes
      pollInterval: 5000, // Check every 5 seconds
    });
  2. Poll manually for very large documents

    const job = await client.ocr.processURL(url);
    
    // Manual polling with custom logic
    while (true) {
      const status = await client.ocr.getJobStatus(job.jobId);
    
      if (status.status === "completed") {
        const result = await client.ocr.getJobResult(job.jobId);
        break;
      }
    
      if (status.status === "failed") {
        throw new Error("Processing failed");
      }
    
      await new Promise((resolve) => setTimeout(resolve, 5000));
    }

Processing Failed

Symptom: Job status is failed

Solutions:

  1. Check the document

    • Ensure it's a valid PDF
    • File isn't corrupted
    • File size is under 50MB
    • Document is readable (not blank or heavily distorted)
  2. Verify schema is valid

    // ❌ Bad - invalid JSON Schema
    {
      "field": "bad-type"
    }
    
    // ✅ Good - valid types
    {
      "field": { "type": "string" }
    }
  3. Check error details

    try {
      const result = await client.ocr.waitUntilDone(job.jobId);
    } catch (error) {
      console.error("Job failed:", error.message);
      // Check job result for details
      const jobResult = await client.ocr.getJobResult(job.jobId);
      console.error("Failure details:", jobResult.error);
    }

Schema & Extraction Issues

Missing or Null Fields

Symptom: Expected fields are null or missing in results

Solutions:

  1. Add field descriptions

    {
      "invoice_date": {
        "type": "string",
        "description": "The invoice date in YYYY-MM-DD format, found in the header"
      }
    }
  2. Use instructions for clarity

    const job = await client.ocr.processURL(url, {
      format: "structured",
      instructions:
        "Extract the total amount from the bottom right of the invoice",
    });
  3. Verify data exists in document

    • Check the markdown output to see raw extracted text
    const job = await client.ocr.processURL(url, {
      format: "markdown",
    });
    const result = await client.ocr.waitUntilDone(job.jobId);
    console.log("Raw text:", result.pages[0].text);

Incorrect Data Types

Symptom: Numbers extracted as strings, or vice versa

Solutions:

{
  "total": {
    "type": "number",
    "description": "Total amount as a number, e.g., 1234.56"
  },
  "invoice_number": {
    "type": "string",
    "description": "Invoice number as text, e.g., INV-2024-001"
  }
}

Array Fields Not Populated

Symptom: Arrays are empty or have fewer items than expected

Solutions:

{
  "line_items": {
    "type": "array",
    "description": "All line items from the invoice table",
    "items": {
      "type": "object",
      "properties": {
        "description": {
          "type": "string",
          "description": "Item description from the first column"
        },
        "amount": {
          "type": "number",
          "description": "Item amount from the last column"
        }
      }
    }
  }
}

Network & Connection Issues

Network Timeout

Symptom: NETWORK_ERROR or connection timeout

Solutions:

  1. Configure retry logic

    const client = new LeapOCR({
      apiKey: process.env.LEAPOCR_API_KEY,
      timeout: 60000, // 60 seconds
      retryConfig: {
        maxRetries: 5,
        retryDelay: 2000,
      },
    });
  2. Check network connectivity

    # Test connection to API
    curl -I https://api.leapocr.com/health
  3. Verify firewall/proxy settings

    • Ensure outbound HTTPS (port 443) is allowed
    • Configure proxy if needed

Rate Limit Exceeded

Symptom: 429 Too Many Requests error

Solutions:

  1. Implement exponential backoff (SDKs do this automatically)

    async function processWithBackoff(url: string, maxRetries = 5) {
      for (let i = 0; i < maxRetries; i++) {
        try {
          return await client.ocr.processURL(url);
        } catch (error) {
          if (error.code === "RATE_LIMIT" && i < maxRetries - 1) {
            const delay = Math.pow(2, i) * 1000; // Exponential backoff
            await new Promise((resolve) => setTimeout(resolve, delay));
            continue;
          }
          throw error;
        }
      }
    }
  2. Batch requests with delays

    async function batchProcess(urls: string[]) {
      const results = [];
      for (const url of urls) {
        results.push(await client.ocr.processURL(url));
        await new Promise((resolve) => setTimeout(resolve, 100)); // 100ms delay
      }
      return results;
    }
  3. Upgrade your plan for higher rate limits


SDK-Specific Issues

TypeScript Type Errors

Issue: Type errors when using the SDK

Solutions:

// Ensure you're importing types correctly
import type { ProcessOptions, OCRResult } from "leapocr";

// Use proper type annotations
const options: ProcessOptions = {
  format: "structured",
  model: "standard-v1",
};

Python Async Issues

Issue: RuntimeError about event loops

Solutions:

# ❌ Bad - nested event loops
async def main():
    result = asyncio.run(process_document())  # Don't do this

# ✅ Good - single event loop
async def main():
    result = await process_document()

if __name__ == "__main__":
    asyncio.run(main())

Go Context Cancellation

Issue: Context deadline exceeded

Solutions:

// Create context with longer timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()

result, err := client.ProcessURL(ctx, url)

File Upload Issues

File Too Large

Symptom: 413 Payload Too Large error

Solutions:

  1. Verify file size

    const MAX_SIZE = 50 * 1024 * 1024; // 50MB
    
    if (file.size > MAX_SIZE) {
      throw new Error("File exceeds 50MB limit");
    }
  2. Compress or split large PDFs

    • Use PDF compression tools
    • Split multi-page documents

Unsupported File Type

Symptom: 400 Bad Request - unsupported file type

Solutions:

const SUPPORTED_TYPES = ["application/pdf"];

if (!SUPPORTED_TYPES.includes(file.type)) {
  throw new Error(`Unsupported file type: ${file.type}`);
}

Multipart Upload Failure

Issue: Direct file upload fails

Solutions:

// Ensure proper FormData usage
const formData = new FormData();
formData.append("file", fileBuffer, "document.pdf");
formData.append("output_type", "structured");

// Don't set Content-Type header manually - let the browser/library do it

Debugging Tips

Enable Debug Logging

Enable detailed logging to diagnose issues
// TypeScript
const client = new LeapOCR({
  apiKey: process.env.LEAPOCR_API_KEY,
  debug: true, // Enable debug logging
});
# Python
import logging
logging.basicConfig(level=logging.DEBUG)

Check Job Status Manually

# Use curl to check job status
curl -H "X-API-Key: your-key" \
  https://api.leapocr.com/ocr/status/job_abc123

Verify API Connectivity

# Test API endpoint
curl -H "X-API-Key: your-key" \
  https://api.leapocr.com/api/v1/health

Review Request/Response

// Log full request/response for debugging
try {
  const result = await client.ocr.processURL(url, options);
  console.log("Success:", JSON.stringify(result, null, 2));
} catch (error) {
  console.error("Request failed:", error);
  console.error("Status:", error.statusCode);
  console.error("Response:", error.response);
}

Getting Help

If you're still experiencing issues:

  1. Check the FAQ: /docs/faq
  2. Review API Reference: /docs/api
  3. Search GitHub Issues:
  4. Contact Support: Through your dashboard with:
    • Job ID (if applicable)
    • Error message
    • Code snippet (remove sensitive data)
    • Expected vs actual behavior

Before contacting support, please ensure you've removed any sensitive information (API keys, personal data) from your examples.