Learn how to handle errors when working with the Lightfast Memory API.

Error Response Format

All errors follow a consistent JSON structure:

json
{
"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.

json
{
"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.

json
{
"error": "authentication_failed",
"message": "Invalid API key"
}

Common causes:

  • Missing API key
  • Invalid or revoked API key
  • Incorrect Authorization header format

403 Forbidden

The API key doesn't have permission for this operation.

json
{
"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.

json
{
"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.

json
{
"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.

json
{
"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.

json
{
"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.

json
{
"error": "service_unavailable",
"message": "Service is temporarily unavailable for maintenance",
"details": {
  "retryAfter": 300
}
}

Error Handling Strategies

Basic Error Handling

typescript
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

typescript
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

typescript
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

typescript
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

typescript
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:

typescript
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:

typescript
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
}
}