Skip to content

Error Handling

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).
CodeHTTP StatusDescription
VALIDATION_ERROR400Invalid request body, missing required fields, or invalid parameter values
AUTHENTICATION_ERROR401Missing or invalid API key / JWT token
AUTHORIZATION_ERROR403Valid credentials but insufficient permissions
NOT_FOUND404The requested resource does not exist
CONFLICT409Resource conflict (e.g., duplicate email on signup)
RATE_LIMIT_EXCEEDED429Too many requests — slow down
USAGE_LIMIT_EXCEEDED429Monthly usage limit reached — upgrade plan or wait
INTERNAL_ERROR500An unexpected error occurred
EXTERNAL_SERVICE_ERROR502An upstream service (embedding, parsing) failed
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}`);
}
}
}

Recommended retry logic for transient errors:

Error CodeRetry?Strategy
RATE_LIMIT_EXCEEDEDYesExponential backoff (1s, 2s, 4s) with jitter
EXTERNAL_SERVICE_ERRORYesRetry once after 1-2 seconds
VALIDATION_ERRORNoFix the request — this won’t resolve on retry
AUTHENTICATION_ERRORNoCheck your API key
NOT_FOUNDNoThe resource doesn’t exist
USAGE_LIMIT_EXCEEDEDNoUpgrade plan or wait for billing period reset
INTERNAL_ERRORNoDo not retry — report to support
CONFLICTNoResolve the conflict (e.g., use a different email)
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;
}
}
}

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"
}
]
}
}
}