Error Handling
Error Envelope
Section titled “Error Envelope”All API errors follow a consistent JSON format:
{ "error": { "code": "ERROR_CODE", "message": "Human-readable description of what went wrong", "details": {} }}code— A machine-readable error code (see table below). Use this for programmatic error handling.message— A human-readable description. Do not parse this — it may change.details— Optional object with additional context (e.g., validation errors, usage limits).
Error Codes
Section titled “Error Codes”| Code | HTTP Status | Description |
|---|---|---|
VALIDATION_ERROR | 400 | Invalid request body, missing required fields, or invalid parameter values |
AUTHENTICATION_ERROR | 401 | Missing or invalid API key / JWT token |
AUTHORIZATION_ERROR | 403 | Valid credentials but insufficient permissions |
NOT_FOUND | 404 | The requested resource does not exist |
CONFLICT | 409 | Resource conflict (e.g., duplicate email on signup) |
RATE_LIMIT_EXCEEDED | 429 | Too many requests — slow down |
USAGE_LIMIT_EXCEEDED | 429 | Monthly usage limit reached — upgrade plan or wait |
INTERNAL_ERROR | 500 | An unexpected error occurred |
EXTERNAL_SERVICE_ERROR | 502 | An upstream service (embedding, parsing) failed |
Handling Errors
Section titled “Handling Errors”import { RAGClient, RAGApiError } from '@rag-api/sdk';
const client = new RAGClient({ apiKey: 'YOUR_API_KEY' });
try { const results = await client.search(kbId, { query: 'test' });} catch (err) { if (err instanceof RAGApiError) { switch (err.code) { case 'RATE_LIMIT_EXCEEDED': // Wait and retry with exponential backoff break; case 'USAGE_LIMIT_EXCEEDED': // Notify user to upgrade plan break; case 'NOT_FOUND': // Knowledge base doesn't exist break; default: console.error(`API error: ${err.code} — ${err.message}`); } }}from rag_api import RAGClient, RAGApiError
client = RAGClient(api_key="YOUR_API_KEY")
try: results = client.search(kb_id, query="test")except RAGApiError as err: if err.code == "RATE_LIMIT_EXCEEDED": # Wait and retry with exponential backoff pass elif err.code == "USAGE_LIMIT_EXCEEDED": # Notify user to upgrade plan pass elif err.code == "NOT_FOUND": # Knowledge base doesn't exist pass else: print(f"API error: {err.code} — {err.message}")Retry Strategy
Section titled “Retry Strategy”Recommended retry logic for transient errors:
| Error Code | Retry? | Strategy |
|---|---|---|
RATE_LIMIT_EXCEEDED | Yes | Exponential backoff (1s, 2s, 4s) with jitter |
EXTERNAL_SERVICE_ERROR | Yes | Retry once after 1-2 seconds |
VALIDATION_ERROR | No | Fix the request — this won’t resolve on retry |
AUTHENTICATION_ERROR | No | Check your API key |
NOT_FOUND | No | The resource doesn’t exist |
USAGE_LIMIT_EXCEEDED | No | Upgrade plan or wait for billing period reset |
INTERNAL_ERROR | No | Do not retry — report to support |
CONFLICT | No | Resolve the conflict (e.g., use a different email) |
Exponential Backoff Example
Section titled “Exponential Backoff Example”async function searchWithRetry( client: RAGClient, kbId: string, query: string, maxRetries = 3,) { for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await client.search(kbId, { query }); } catch (err) { if ( err instanceof RAGApiError && err.code === 'RATE_LIMIT_EXCEEDED' && attempt < maxRetries ) { const delay = Math.pow(2, attempt) * 1000 + Math.random() * 500; await new Promise((r) => setTimeout(r, delay)); continue; } throw err; } }}import timeimport randomfrom rag_api import RAGClient, RAGApiError
def search_with_retry(client, kb_id, query, max_retries=3): for attempt in range(max_retries + 1): try: return client.search(kb_id, query=query) except RAGApiError as err: if err.code == "RATE_LIMIT_EXCEEDED" and attempt < max_retries: delay = (2 ** attempt) + random.uniform(0, 0.5) time.sleep(delay) continue raiseValidation Error Details
Section titled “Validation Error Details”VALIDATION_ERROR responses include a details object with field-level information:
{ "error": { "code": "VALIDATION_ERROR", "message": "Invalid request body", "details": { "issues": [ { "path": ["query"], "message": "Required" }, { "path": ["top_k"], "message": "Number must be less than or equal to 50" } ] } }}