Skip to main content

Error Handling

Understand and handle ZenSearch API errors.

Error Response Format

All errors follow a consistent format:

{
"error": {
"code": "error_code",
"message": "Human-readable error message",
"details": {
"field": "Additional context"
}
},
"meta": {
"requestId": "req_abc123"
}
}

HTTP Status Codes

CodeDescription
400Bad Request - Invalid parameters
401Unauthorized - Authentication required
403Forbidden - Insufficient permissions
404Not Found - Resource doesn't exist
409Conflict - Resource already exists
422Unprocessable - Validation failed
429Too Many Requests - Rate limited
500Internal Error - Server error
503Service Unavailable - Temporarily down

Error Codes

Authentication Errors

CodeDescription
unauthorizedMissing or invalid API key
token_expiredAPI key has expired
invalid_tokenMalformed token

Permission Errors

CodeDescription
forbiddenLacking required permissions
team_access_deniedNo access to team
resource_access_deniedNo access to resource

Validation Errors

CodeDescription
invalid_requestRequest body is malformed
missing_parameterRequired parameter missing
invalid_parameterParameter value is invalid
validation_failedMultiple validation errors

Resource Errors

CodeDescription
not_foundResource doesn't exist
already_existsResource already exists
resource_deletedResource was deleted

Rate Limit Errors

CodeDescription
rate_limit_exceededToo many requests
quota_exceededPlan quota exceeded

Server Errors

CodeDescription
internal_errorUnexpected server error
service_unavailableService temporarily down
timeoutRequest timed out

Handling Errors

JavaScript/TypeScript

import { ZenSearchError } from '@zensearch/sdk';

try {
const result = await client.search({ query: 'test' });
} catch (error) {
if (error instanceof ZenSearchError) {
switch (error.code) {
case 'rate_limit_exceeded':
await sleep(error.retryAfter * 1000);
// Retry
break;
case 'unauthorized':
// Refresh API key
break;
default:
console.error('API error:', error.message);
}
}
}

Python

from zensearch import ZenSearchError

try:
result = client.search(query="test")
except ZenSearchError as e:
if e.code == "rate_limit_exceeded":
time.sleep(e.retry_after)
# Retry
elif e.code == "unauthorized":
# Handle auth error
pass
else:
print(f"API error: {e.message}")

Validation Error Details

Validation errors include field-specific details:

{
"error": {
"code": "validation_failed",
"message": "Request validation failed",
"details": {
"errors": [
{
"field": "query",
"message": "Query is required"
},
{
"field": "limit",
"message": "Limit must be between 1 and 100"
}
]
}
}
}

Retry Strategy

Retryable Errors

CodeRetry
429Yes, after Retry-After
500Yes, with backoff
503Yes, with backoff
400No
401No
403No
404No

Backoff Example

async function withRetry<T>(
fn: () => Promise<T>,
maxRetries = 3
): Promise<T> {
let lastError: Error;

for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;

if (!isRetryable(error)) {
throw error;
}

const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
await sleep(delay);
}
}

throw lastError;
}

Debugging

Request ID

Every response includes a request ID:

X-Request-ID: req_abc123

Include this in support requests for faster debugging.

Logging

Log errors with context:

logger.error('API request failed', {
requestId: error.requestId,
code: error.code,
message: error.message,
endpoint: '/v1/search',
params: { query: 'test' }
});

Next Steps