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:
-
Verify your API key is correct
# Check that your key is properly set echo $LEAPOCR_API_KEY -
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() }); -
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:
-
Increase timeout for large documents
const result = await client.ocr.waitUntilDone(job.jobId, { timeout: 300000, // 5 minutes pollInterval: 5000, // Check every 5 seconds }); -
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:
-
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)
-
Verify schema is valid
// ❌ Bad - invalid JSON Schema { "field": "bad-type" } // ✅ Good - valid types { "field": { "type": "string" } } -
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:
-
Add field descriptions
{ "invoice_date": { "type": "string", "description": "The invoice date in YYYY-MM-DD format, found in the header" } } -
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", }); -
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:
-
Configure retry logic
const client = new LeapOCR({ apiKey: process.env.LEAPOCR_API_KEY, timeout: 60000, // 60 seconds retryConfig: { maxRetries: 5, retryDelay: 2000, }, }); -
Check network connectivity
# Test connection to API curl -I https://api.leapocr.com/health -
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:
-
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; } } } -
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; } -
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:
-
Verify file size
const MAX_SIZE = 50 * 1024 * 1024; // 50MB if (file.size > MAX_SIZE) { throw new Error("File exceeds 50MB limit"); } -
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 itDebugging Tips
Enable Debug Logging
// 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_abc123Verify API Connectivity
# Test API endpoint
curl -H "X-API-Key: your-key" \
https://api.leapocr.com/api/v1/healthReview 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:
- Check the FAQ: /docs/faq
- Review API Reference: /docs/api
- Search GitHub Issues:
- 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.