Back to Guides

Error Handling

Guide

Handle API errors gracefully with typed exceptions, rate limit retries, and idempotency keys. Examples shown for both Node.js and Python.

Error Codes Reference

CodeHTTPDescriptionNode.jsPython
invalid_email400Email format is invalidBearLumenApiErrorInvalidEmailError
duplicate_customer400End user with this external ID already existsBearLumenApiErrorDuplicateCustomerError
invalid_request400Request parameters are invalidBearLumenApiErrorInvalidRequestError
authentication_error401API key is invalid or missingBearLumenApiErrorAuthenticationError
not_found404Requested resource does not existBearLumenApiErrorNotFoundError
rate_limit_exceeded429Too many requests, retry after delayBearLumenApiErrorRateLimitError
server_error500Internal server errorBearLumenApiErrorBearLumenError
network_errorNetwork connectivity issueBearLumenApiErrorNetworkError

Basic Error Handling

Use instanceof checks with BearLumenApiError and switch on the error.code property:

import { BearLumen, BearLumenApiError } from '@bearlumen/node-sdk';

try {
  const { endUser } = await bearLumen.endUsers.create({
    externalId: 'user_123',
    name: 'AI Startup Inc',
  });
} catch (error) {
  if (error instanceof BearLumenApiError) {
    switch (error.code) {
      case 'invalid_email':
        console.error('Invalid email format');
        break;
      case 'duplicate_customer':
        console.error('End user already exists');
        break;
      case 'rate_limit_exceeded':
        console.error(`Rate limit hit, retry after ${error.retryAfter} seconds`);
        break;
      case 'authentication_error':
        console.error('Invalid API key');
        break;
      default:
        console.error(`Error: ${error.message}`);
    }
  }
}

Rate Limit Handling

When you hit a rate limit, the error.retryAfter property tells you how long to wait. Here is a simple retry pattern with exponential backoff:

import { BearLumenApiError } from '@bearlumen/node-sdk';

async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3,
): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (
        error instanceof BearLumenApiError &&
        error.code === 'rate_limit_exceeded' &&
        attempt < maxRetries
      ) {
        const delay = error.retryAfter
          ? error.retryAfter * 1000
          : Math.pow(2, attempt) * 1000;
        console.log(`Rate limited. Retrying in ${delay}ms...`);
        await new Promise((resolve) => setTimeout(resolve, delay));
        continue;
      }
      throw error;
    }
  }
  throw new Error('Max retries exceeded');
}

// Usage
const result = await withRetry(() =>
  bearLumen.usage.record({
    eventType: 'token_usage',
    metricName: 'llm_tokens',
    quantity: 1500,
    idempotencyKey: 'unique-key',
  })
);

Idempotency Keys

Idempotency keys prevent duplicate billing when the same usage event is sent more than once. Use deterministic keys based on your business entities (invoice IDs, line numbers, etc.) rather than random values:

// Use deterministic keys to prevent duplicate billing
await bearLumen.usage.record({
  eventType: 'token_usage',
  metricName: 'llm_tokens',
  quantity: 1500,
  idempotencyKey: `invoice-${invoiceId}-line-${lineNum}`,
});

// Check if the event was actually recorded
const result = await bearLumen.usage.record({
  eventType: 'token_usage',
  metricName: 'llm_tokens',
  quantity: 1500,
  idempotencyKey: 'same-key-again',
});

if (!result.recorded) {
  console.log('Duplicate event skipped');
}

Tip: Duplicate events return recorded: false. Always check this value after recording usage events.

Best Practices

  • 1

    Always use idempotency keys for usage recording. Deterministic keys based on your business entities prevent duplicate billing from retries or network issues.

  • 2

    Handle rate limits with exponential backoff. Respect the retryAfter / retry_after value when available, otherwise use increasing delays.

  • 3

    Log error codes and messages for debugging. Error codes are stable identifiers you can use in alerting and log analysis.

  • 4

    Use specific error types over generic catch-all. In Python, catch specific exceptions like RateLimitError before BearLumenError. In Node.js, check error.code for specific handling.

  • 5

    Check the recorded flag after usage.record() calls. Use result.recorded (Node.js) or result['recorded'] (Python) to confirm the event was actually recorded.