Error Handling
Learn how to handle errors gracefully when using the Knowhere API.
Error Response Format
All API errors follow a consistent format:
{
"success": false,
"error": {
"code": "INVALID_ARGUMENT",
"message": "source_url must be a valid HTTP or HTTPS URL",
"request_id": "req_abc123def456",
"details": {
"field": "source_url",
"reason": "INVALID_URL_FORMAT"
}
}
}
| Field | Description |
|---|---|
code | Machine-readable error code |
message | Human-readable description |
request_id | Unique ID for debugging/support |
details | Additional context (optional) |
Error Codes Reference
Client Errors (4xx)
| Code | HTTP Status | Description | Retryable |
|---|---|---|---|
INVALID_ARGUMENT | 400 | Invalid request parameters | No |
UNAUTHENTICATED | 401 | Invalid or missing API key | No* |
PERMISSION_DENIED | 403 | Insufficient permissions | No |
NOT_FOUND | 404 | Resource not found | No |
ALREADY_EXISTS | 409 | Resource already exists | No |
RESOURCE_EXHAUSTED | 429 | Rate limit or quota exceeded | Yes |
Server Errors (5xx)
| Code | HTTP Status | Description | Retryable |
|---|---|---|---|
INTERNAL_ERROR | 500 | Internal server error | Yes |
UNAVAILABLE | 503 | Service temporarily unavailable | Yes |
DEADLINE_EXCEEDED | 504 | Request timeout | Yes |
*Retryable after fixing authentication
Handling Errors in Code
- Python
- Node.js
import requests
from typing import Optional
class KnowhereError(Exception):
"""Base exception for Knowhere API errors."""
def __init__(self, code: str, message: str, request_id: str, details: Optional[dict] = None):
self.code = code
self.message = message
self.request_id = request_id
self.details = details or {}
super().__init__(f"[{code}] {message} (request_id: {request_id})")
class AuthenticationError(KnowhereError):
"""Invalid or missing API key."""
pass
class RateLimitError(KnowhereError):
"""Rate limit exceeded."""
def __init__(self, *args, retry_after: int = 60, **kwargs):
super().__init__(*args, **kwargs)
self.retry_after = retry_after
class NotFoundError(KnowhereError):
"""Resource not found."""
pass
def make_request(method: str, url: str, api_key: str, **kwargs) -> dict:
"""Make an API request with error handling."""
response = requests.request(
method,
url,
headers={"Authorization": f"Bearer {api_key}"},
**kwargs
)
if response.status_code >= 400:
error_data = response.json().get("error", {})
code = error_data.get("code", "UNKNOWN")
message = error_data.get("message", "Unknown error")
request_id = error_data.get("request_id", "unknown")
details = error_data.get("details", {})
if response.status_code == 401:
raise AuthenticationError(code, message, request_id, details)
elif response.status_code == 404:
raise NotFoundError(code, message, request_id, details)
elif response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
raise RateLimitError(code, message, request_id, details, retry_after=retry_after)
else:
raise KnowhereError(code, message, request_id, details)
return response.json()
# Usage Example
def create_job_safe(source_url: str, api_key: str) -> dict:
"""Create a job with comprehensive error handling."""
try:
return make_request(
"POST",
"https://api.knowhereto.ai/v1/jobs",
api_key,
json={"source_type": "url", "source_url": source_url}
)
except AuthenticationError as e:
print(f"Authentication failed: {e.message}")
print("Please check your API key in the dashboard.")
raise
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after} seconds.")
raise
except NotFoundError as e:
print(f"Resource not found: {e.message}")
raise
except KnowhereError as e:
print(f"API error [{e.code}]: {e.message}")
print(f"Request ID: {e.request_id}")
print("Please contact support with the request ID.")
raise
class KnowhereError extends Error {
constructor(code, message, requestId, details = {}) {
super(`[${code}] ${message} (request_id: ${requestId})`);
this.code = code;
this.requestId = requestId;
this.details = details;
}
}
class AuthenticationError extends KnowhereError {}
class RateLimitError extends KnowhereError {
constructor(code, message, requestId, details, retryAfter) {
super(code, message, requestId, details);
this.retryAfter = retryAfter;
}
}
class NotFoundError extends KnowhereError {}
async function makeRequest(method, url, apiKey, options = {}) {
const response = await fetch(url, {
method,
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
...options.headers
},
...options
});
if (!response.ok) {
const data = await response.json();
const error = data.error || {};
const { code = 'UNKNOWN', message = 'Unknown error', request_id, details } = error;
if (response.status === 401) {
throw new AuthenticationError(code, message, request_id, details);
} else if (response.status === 404) {
throw new NotFoundError(code, message, request_id, details);
} else if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
throw new RateLimitError(code, message, request_id, details, retryAfter);
} else {
throw new KnowhereError(code, message, request_id, details);
}
}
return response.json();
}
Retry Strategies
Simple Retry with Backoff
- Python
- Node.js
import time
from functools import wraps
def retry_with_backoff(max_retries: int = 3, base_delay: float = 1.0):
"""Decorator to retry failed requests with exponential backoff."""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except RateLimitError as e:
# Use server-provided retry delay
delay = e.retry_after
print(f"Rate limited. Waiting {delay}s...")
time.sleep(delay)
last_exception = e
except KnowhereError as e:
# Only retry server errors
if e.code not in ["INTERNAL_ERROR", "UNAVAILABLE", "DEADLINE_EXCEEDED"]:
raise
delay = base_delay * (2 ** attempt)
print(f"Server error. Retrying in {delay}s... (attempt {attempt + 1}/{max_retries})")
time.sleep(delay)
last_exception = e
raise last_exception
return wrapper
return decorator
# Usage
@retry_with_backoff(max_retries=3)
def create_job(source_url: str, api_key: str) -> dict:
return make_request(
"POST",
"https://api.knowhereto.ai/v1/jobs",
api_key,
json={"source_type": "url", "source_url": source_url}
)
async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1000) {
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
if (error instanceof RateLimitError) {
// Use server-provided retry delay
const delay = error.retryAfter * 1000;
console.log(`Rate limited. Waiting ${error.retryAfter}s...`);
await new Promise(r => setTimeout(r, delay));
} else if (error instanceof KnowhereError) {
// Only retry server errors
const retryableCodes = ['INTERNAL_ERROR', 'UNAVAILABLE', 'DEADLINE_EXCEEDED'];
if (!retryableCodes.includes(error.code)) {
throw error;
}
const delay = baseDelay * Math.pow(2, attempt);
console.log(`Server error. Retrying in ${delay}ms... (attempt ${attempt + 1}/${maxRetries})`);
await new Promise(r => setTimeout(r, delay));
} else {
throw error;
}
}
}
throw lastError;
}
// Usage
async function createJob(sourceUrl, apiKey) {
return retryWithBackoff(() =>
makeRequest('POST', 'https://api.knowhereto.ai/v1/jobs', apiKey, {
body: JSON.stringify({ source_type: 'url', source_url: sourceUrl })
})
);
}
Common Error Scenarios
Invalid File Format
{
"error": {
"code": "INVALID_ARGUMENT",
"message": "Unsupported file format: .xyz",
"request_id": "req_abc123",
"details": {
"field": "file_name",
"reason": "UNSUPPORTED_FORMAT",
"supported_formats": [".pdf", ".docx", ".xlsx", ".pptx"]
}
}
}
Solution: Use a supported file format.
URL Not Accessible
{
"error": {
"code": "INVALID_ARGUMENT",
"message": "Unable to fetch document from URL: Connection refused",
"request_id": "req_def456",
"details": {
"field": "source_url",
"reason": "URL_NOT_ACCESSIBLE"
}
}
}
Solution: Ensure the URL is publicly accessible.
Job Processing Failed
{
"job_id": "job_xyz789",
"status": "failed",
"error": {
"code": "INTERNAL_ERROR",
"message": "Unable to parse document: file appears to be corrupted"
}
}
Solution: Verify the document opens correctly locally. Try re-uploading.
Rate Limit Exceeded
{
"error": {
"code": "RESOURCE_EXHAUSTED",
"message": "Rate limit exceeded",
"request_id": "req_ghi789",
"details": {
"retry_after": 15,
"limit": 60,
"period": "minute"
}
}
}
Solution: Wait for the specified time, then retry.
Debugging Tips
- Always log the
request_id: Include it when contacting support - Check the
detailsobject: Often contains specific field-level errors - Verify API key: Test with a simple GET request first
- Check file format: Ensure you're using supported formats
- Test URL accessibility: Verify the URL works from a browser
Getting Help
If you encounter persistent errors:
- Note the
request_idfrom the error response - Check the status page for outages
- Contact support at team@knowhereto.ai
Include:
- The full error response
- The request you made (without your API key)
- When the error occurred
- Any relevant context
Next Steps
- API Overview - Full error code reference
- Polling Guide - Handle polling errors