Learn how to handle errors when working with the Lightfast Memory API.
Error Response Format
All errors follow a consistent JSON structure:
{
"error": "rate_limit_exceeded",
"message": "Rate limit exceeded. Retry after 42 seconds.",
"details": {
"retryAfter": 42,
"limit": 600,
"remaining": 0
},
"requestId": "req_abc123xyz"
}Error Codes
400 Bad Request
The request was invalid or malformed.
{
"error": "invalid_request",
"message": "Content exceeds maximum length of 8000 tokens",
"details": {
"field": "content",
"maxTokens": 8000,
"providedTokens": 9245
}
}Common causes:
- Missing required parameters
- Invalid parameter types
- Content too large
- Malformed JSON
401 Unauthorized
Authentication failed or API key is invalid.
{
"error": "authentication_failed",
"message": "Invalid API key"
}Common causes:
- Missing API key
- Invalid or revoked API key
- Incorrect
Authorizationheader format
403 Forbidden
The API key doesn't have permission for this operation.
{
"error": "permission_denied",
"message": "This key does not have write access to collection 'github-private'"
}Common causes:
- Using publishable key for write operations
- Accessing restricted collections
- Exceeding key scope permissions
404 Not Found
The requested resource doesn't exist.
{
"error": "resource_not_found",
"message": "Memory with id 'pr-127' not found in collection 'github-lightfast'"
}Common causes:
- Invalid memory ID
- Non-existent collection
- Memory was deleted
409 Conflict
The resource already exists.
{
"error": "conflict",
"message": "Memory with id 'pr-127' already exists in collection 'github-lightfast'"
}Solution: Use updateMemory instead of addMemory for existing memories.
429 Too Many Requests
Rate limit exceeded.
{
"error": "rate_limit_exceeded",
"message": "Rate limit exceeded. Retry after 42 seconds.",
"details": {
"retryAfter": 42,
"limit": 600,
"remaining": 0,
"reset": 1642694400
}
}Solution: Implement exponential backoff and retry logic.
500 Internal Server Error
An unexpected error occurred on the server.
{
"error": "internal_error",
"message": "An unexpected error occurred",
"requestId": "req_abc123xyz"
}Solution: Retry with exponential backoff. If persists, contact support with the requestId.
503 Service Unavailable
The service is temporarily unavailable.
{
"error": "service_unavailable",
"message": "Service is temporarily unavailable for maintenance",
"details": {
"retryAfter": 300
}
}Error Handling Strategies
Basic Error Handling
try {
const result = await memory.addMemory({
collection: 'github-lightfast',
id: 'pr-127',
content: 'PR content...'
})
} catch (error) {
if (error.status === 409) {
// Memory exists, update instead
await memory.updateMemory({
collection: 'github-lightfast',
id: 'pr-127',
content: 'Updated content...'
})
} else {
console.error('Failed to add memory:', error.message)
throw error
}
}Retry with Exponential Backoff
async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries = 3,
initialDelay = 1000
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn()
} catch (error: any) {
const isLastAttempt = i === maxRetries - 1
// Don't retry on client errors (4xx)
if (error.status >= 400 && error.status < 500) {
throw error
}
if (isLastAttempt) {
throw error
}
// Use retry-after header if available
const retryAfter = error.details?.retryAfter
const delay = retryAfter
? retryAfter * 1000
: initialDelay * Math.pow(2, i)
console.log(`Retry attempt ${i + 1} after ${delay}ms`)
await new Promise(resolve => setTimeout(resolve, delay))
}
}
throw new Error('Max retries exceeded')
}
// Usage
const result = await retryWithBackoff(async () => {
return await memory.searchMemory({
query: 'auth refactor'
})
})Comprehensive Error Handler
class MemoryAPIError extends Error {
constructor(
public code: string,
message: string,
public status: number,
public details?: any,
public requestId?: string
) {
super(message)
this.name = 'MemoryAPIError'
}
}
async function handleAPIError(error: any): Promise<never> {
const { error: code, message, details, requestId } = error.response?.data || {}
switch (error.status) {
case 400:
throw new MemoryAPIError(
code || 'invalid_request',
message || 'Invalid request',
400,
details,
requestId
)
case 401:
throw new MemoryAPIError(
code || 'authentication_failed',
message || 'Authentication failed',
401,
details,
requestId
)
case 403:
throw new MemoryAPIError(
code || 'permission_denied',
message || 'Permission denied',
403,
details,
requestId
)
case 404:
throw new MemoryAPIError(
code || 'resource_not_found',
message || 'Resource not found',
404,
details,
requestId
)
case 409:
throw new MemoryAPIError(
code || 'conflict',
message || 'Resource already exists',
409,
details,
requestId
)
case 429:
throw new MemoryAPIError(
code || 'rate_limit_exceeded',
message || 'Rate limit exceeded',
429,
details,
requestId
)
case 500:
case 502:
case 503:
case 504:
throw new MemoryAPIError(
code || 'internal_error',
message || 'Internal server error',
error.status,
details,
requestId
)
default:
throw new MemoryAPIError(
'unknown_error',
message || 'An unknown error occurred',
error.status || 500,
details,
requestId
)
}
}Rate Limit Handler
class RateLimiter {
private requestCount = 0
private resetTime = Date.now() + 60000 // 1 minute
private readonly limit = 600 // requests per minute
async checkLimit(): Promise<void> {
const now = Date.now()
// Reset counter if time window passed
if (now >= this.resetTime) {
this.requestCount = 0
this.resetTime = now + 60000
}
// Check if limit exceeded
if (this.requestCount >= this.limit) {
const waitTime = this.resetTime - now
throw new Error(`Rate limit exceeded. Wait ${waitTime}ms`)
}
this.requestCount++
}
updateFromHeaders(headers: Headers) {
const remaining = parseInt(headers.get('X-RateLimit-Remaining') || '0')
const reset = parseInt(headers.get('X-RateLimit-Reset') || '0') * 1000
if (remaining === 0 && reset > Date.now()) {
this.requestCount = this.limit
this.resetTime = reset
}
}
}
const rateLimiter = new RateLimiter()
async function makeRequest(params: any) {
await rateLimiter.checkLimit()
try {
const response = await fetch(url, options)
rateLimiter.updateFromHeaders(response.headers)
return response
} catch (error) {
// Handle error
throw error
}
}Error Logging
Structured Logging
interface ErrorLog {
timestamp: string
requestId?: string
errorCode: string
message: string
status: number
details?: any
stack?: string
context: {
operation: string
collection?: string
memoryId?: string
}
}
function logError(error: MemoryAPIError, context: any): void {
const errorLog: ErrorLog = {
timestamp: new Date().toISOString(),
requestId: error.requestId,
errorCode: error.code,
message: error.message,
status: error.status,
details: error.details,
stack: error.stack,
context
}
// Send to logging service
console.error('API Error:', JSON.stringify(errorLog, null, 2))
// Send to error tracking (e.g., Sentry)
if (typeof window !== 'undefined' && window.Sentry) {
window.Sentry.captureException(error, {
tags: {
errorCode: error.code,
status: error.status,
requestId: error.requestId
},
extra: errorLog
})
}
}Common Patterns
Idempotent Operations
Make operations idempotent to handle retries safely:
async function upsertMemory(params: any) {
try {
// Try to add first
return await memory.addMemory(params)
} catch (error: any) {
if (error.status === 409) {
// Already exists, update instead
return await memory.updateMemory({
collection: params.collection,
id: params.id,
content: params.content,
metadata: params.metadata
})
}
throw error
}
}Graceful Degradation
Provide fallback behavior when the API is unavailable:
async function searchWithFallback(query: string) {
try {
// Try API search first
return await memory.searchMemory({ query })
} catch (error: any) {
if (error.status >= 500 || error.code === 'service_unavailable') {
// Fall back to cached results
console.warn('API unavailable, using cached results')
return getCachedResults(query)
}
throw error
}
}