CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-langfuse--core

Core functions and utilities for Langfuse packages including API client, logging, media handling, and OpenTelemetry tracing attributes

Overview
Eval results
Files

errors.mddocs/

Error Handling

Structured error classes for API interactions with status codes, response bodies, timeout handling, and HTTP-specific error types.

Capabilities

LangfuseAPIError

Base error class for all API-related errors with detailed context about the failed request.

class LangfuseAPIError extends Error {
  readonly statusCode?: number;
  readonly body?: unknown;
  readonly rawResponse?: RawResponse;

  constructor(params: {
    message?: string;
    statusCode?: number;
    body?: unknown;
    rawResponse?: RawResponse;
  });
}

interface RawResponse {
  statusCode: number;
  headers: Record<string, string>;
  body: unknown;
}

Import:

import { LangfuseAPIError } from '@langfuse/core';

Constructor

Creates a new API error with context about the failed request.

constructor(params: {
  message?: string;
  statusCode?: number;
  body?: unknown;
  rawResponse?: RawResponse;
})

Parameters:

  • message (optional) - Human-readable error message
  • statusCode (optional) - HTTP status code (e.g., 400, 404, 500)
  • body (optional) - Response body from the API (may contain error details)
  • rawResponse (optional) - Complete raw HTTP response

Properties:

  • statusCode?: number - HTTP status code of the failed request
  • body?: unknown - Response body (often contains error details from API)
  • rawResponse?: RawResponse - Complete raw response with headers and body
  • message: string - Error message (inherited from Error)
  • name: string - Error name "LangfuseAPIError" (inherited from Error)
  • stack?: string - Stack trace (inherited from Error)

Usage Examples:

import { LangfuseAPIClient, LangfuseAPIError } from '@langfuse/core';

const client = new LangfuseAPIClient({ /* ... */ });

try {
  const trace = await client.trace.get('invalid-id');
} catch (error) {
  if (error instanceof LangfuseAPIError) {
    console.error('API Error:', error.message);
    console.error('Status Code:', error.statusCode);
    console.error('Response Body:', error.body);

    // Access detailed error information
    if (error.statusCode === 404) {
      console.log('Trace not found');
    } else if (error.statusCode === 429) {
      console.log('Rate limited');
    } else if (error.statusCode && error.statusCode >= 500) {
      console.log('Server error');
    }

    // Log raw response for debugging
    if (error.rawResponse) {
      console.error('Raw Response:', {
        status: error.rawResponse.statusCode,
        headers: error.rawResponse.headers,
        body: error.rawResponse.body
      });
    }
  } else {
    console.error('Unexpected error:', error);
  }
}

LangfuseAPITimeoutError

Error thrown when API requests exceed the configured timeout duration.

class LangfuseAPITimeoutError extends Error {
  constructor(message: string);
}

Import:

import { LangfuseAPITimeoutError } from '@langfuse/core';

Constructor

Creates a new timeout error.

constructor(message: string)

Parameters:

  • message - Description of the timeout (includes duration and operation)

Properties:

  • message: string - Error message describing the timeout
  • name: string - Error name "LangfuseAPITimeoutError" (inherited from Error)
  • stack?: string - Stack trace (inherited from Error)

Usage Examples:

import {
  LangfuseAPIClient,
  LangfuseAPITimeoutError,
  LangfuseAPIError
} from '@langfuse/core';

const client = new LangfuseAPIClient({ /* ... */ });

try {
  const trace = await client.trace.get('trace-id', {
    timeoutInSeconds: 5  // 5 second timeout
  });
} catch (error) {
  if (error instanceof LangfuseAPITimeoutError) {
    console.error('Request timed out:', error.message);
    // Implement retry logic or use cached data
  } else if (error instanceof LangfuseAPIError) {
    console.error('API error:', error.statusCode);
  } else {
    console.error('Unexpected error:', error);
  }
}

HTTP-Specific Error Classes

The API exports specific error classes for common HTTP status codes, all extending LangfuseAPIError.

class Error extends LangfuseAPIError {
  // Generic error (400 Bad Request)
}

class UnauthorizedError extends LangfuseAPIError {
  // 401 Unauthorized - Invalid credentials
}

class AccessDeniedError extends LangfuseAPIError {
  // 403 Forbidden - Insufficient permissions
}

class NotFoundError extends LangfuseAPIError {
  // 404 Not Found - Resource doesn't exist
}

class MethodNotAllowedError extends LangfuseAPIError {
  // 405 Method Not Allowed - HTTP method not supported
}

Import:

import {
  Error,
  UnauthorizedError,
  AccessDeniedError,
  NotFoundError,
  MethodNotAllowedError
} from '@langfuse/core';

Note: The generic Error class name may conflict with JavaScript's built-in Error. Consider importing with an alias:

import { Error as LangfuseError } from '@langfuse/core';

Usage Examples:

import {
  LangfuseAPIClient,
  UnauthorizedError,
  AccessDeniedError,
  NotFoundError
} from '@langfuse/core';

const client = new LangfuseAPIClient({ /* ... */ });

async function getTraceWithHandling(traceId: string) {
  try {
    return await client.trace.get(traceId);
  } catch (error) {
    if (error instanceof UnauthorizedError) {
      console.error('Invalid API credentials');
      // Refresh credentials or prompt user to login
      throw new Error('Please check your API keys');
    }

    if (error instanceof AccessDeniedError) {
      console.error('Insufficient permissions to access trace');
      // Request additional permissions
      throw new Error('You do not have permission to view this trace');
    }

    if (error instanceof NotFoundError) {
      console.log('Trace not found, returning null');
      return null;  // Handle gracefully
    }

    // Rethrow other errors
    throw error;
  }
}

Usage Patterns

Comprehensive Error Handling

import {
  LangfuseAPIClient,
  LangfuseAPIError,
  LangfuseAPITimeoutError,
  UnauthorizedError,
  AccessDeniedError,
  NotFoundError
} from '@langfuse/core';

async function robustAPICall<T>(
  operation: () => Promise<T>,
  fallback?: T
): Promise<T | undefined> {
  try {
    return await operation();
  } catch (error) {
    // Handle specific error types
    if (error instanceof LangfuseAPITimeoutError) {
      console.warn('Request timed out, using fallback');
      return fallback;
    }

    if (error instanceof UnauthorizedError) {
      console.error('Authentication failed');
      throw new Error('Please check your API credentials');
    }

    if (error instanceof AccessDeniedError) {
      console.error('Access denied');
      throw new Error('Insufficient permissions');
    }

    if (error instanceof NotFoundError) {
      console.log('Resource not found, using fallback');
      return fallback;
    }

    if (error instanceof LangfuseAPIError) {
      // Generic API error
      console.error(`API Error [${error.statusCode}]:`, error.message);

      if (error.statusCode === 429) {
        console.warn('Rate limited, please retry later');
        throw new Error('Rate limit exceeded');
      }

      if (error.statusCode && error.statusCode >= 500) {
        console.error('Server error, using fallback');
        return fallback;
      }

      console.error('Response body:', error.body);
      throw error;
    }

    // Unknown error
    console.error('Unexpected error:', error);
    throw error;
  }
}

// Usage
const client = new LangfuseAPIClient({ /* ... */ });

const trace = await robustAPICall(
  () => client.trace.get('trace-id'),
  null  // Fallback value
);

Retry with Exponential Backoff

import {
  LangfuseAPIClient,
  LangfuseAPIError,
  LangfuseAPITimeoutError
} from '@langfuse/core';

async function retryWithBackoff<T>(
  operation: () => Promise<T>,
  maxRetries: number = 3,
  initialDelay: number = 1000
): Promise<T> {
  let lastError: Error | undefined;

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

      // Don't retry on client errors (4xx)
      if (error instanceof LangfuseAPIError) {
        if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
          // Don't retry 4xx errors (except 429 rate limit)
          if (error.statusCode !== 429) {
            throw error;
          }
        }
      }

      // Don't retry on final attempt
      if (attempt === maxRetries - 1) {
        break;
      }

      // Calculate delay with exponential backoff
      const delay = initialDelay * Math.pow(2, attempt);
      console.log(`Attempt ${attempt + 1} failed, retrying in ${delay}ms...`);

      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }

  throw lastError!;
}

// Usage
const client = new LangfuseAPIClient({ /* ... */ });

const trace = await retryWithBackoff(
  () => client.trace.get('trace-id'),
  5,     // Max 5 attempts
  1000   // Start with 1 second delay
);

Error Logging and Monitoring

import {
  LangfuseAPIClient,
  LangfuseAPIError,
  LangfuseAPITimeoutError,
  getGlobalLogger
} from '@langfuse/core';

const logger = getGlobalLogger();

async function monitoredAPICall<T>(
  operationName: string,
  operation: () => Promise<T>
): Promise<T> {
  const startTime = Date.now();

  try {
    logger.debug(`Starting ${operationName}`);
    const result = await operation();
    const duration = Date.now() - startTime;
    logger.info(`${operationName} completed in ${duration}ms`);
    return result;
  } catch (error) {
    const duration = Date.now() - startTime;

    if (error instanceof LangfuseAPITimeoutError) {
      logger.error(`${operationName} timed out after ${duration}ms`);
      // Send to monitoring service
      sendToMonitoring({
        operation: operationName,
        error: 'timeout',
        duration
      });
    } else if (error instanceof LangfuseAPIError) {
      logger.error(
        `${operationName} failed with status ${error.statusCode}`,
        error.body
      );
      // Send to monitoring service
      sendToMonitoring({
        operation: operationName,
        error: 'api_error',
        statusCode: error.statusCode,
        duration
      });
    } else {
      logger.error(`${operationName} failed with unexpected error`, error);
      // Send to monitoring service
      sendToMonitoring({
        operation: operationName,
        error: 'unexpected',
        duration
      });
    }

    throw error;
  }
}

// Usage
const trace = await monitoredAPICall(
  'fetch_trace',
  () => client.trace.get('trace-id')
);

Circuit Breaker Pattern

import { LangfuseAPIClient, LangfuseAPIError } from '@langfuse/core';

class CircuitBreaker {
  private failureCount = 0;
  private lastFailureTime = 0;
  private state: 'closed' | 'open' | 'half-open' = 'closed';

  constructor(
    private threshold: number = 5,
    private timeout: number = 60000
  ) {}

  async execute<T>(operation: () => Promise<T>): Promise<T> {
    if (this.state === 'open') {
      if (Date.now() - this.lastFailureTime > this.timeout) {
        this.state = 'half-open';
        this.failureCount = 0;
      } else {
        throw new Error('Circuit breaker is open');
      }
    }

    try {
      const result = await operation();

      if (this.state === 'half-open') {
        this.state = 'closed';
      }

      this.failureCount = 0;
      return result;
    } catch (error) {
      this.failureCount++;
      this.lastFailureTime = Date.now();

      if (this.failureCount >= this.threshold) {
        this.state = 'open';
        console.error('Circuit breaker opened due to repeated failures');
      }

      throw error;
    }
  }
}

// Usage
const client = new LangfuseAPIClient({ /* ... */ });
const breaker = new CircuitBreaker(5, 60000);

async function getTraceWithCircuitBreaker(traceId: string) {
  return breaker.execute(() => client.trace.get(traceId));
}

User-Friendly Error Messages

import {
  LangfuseAPIError,
  UnauthorizedError,
  AccessDeniedError,
  NotFoundError,
  LangfuseAPITimeoutError
} from '@langfuse/core';

function getUserFriendlyMessage(error: unknown): string {
  if (error instanceof UnauthorizedError) {
    return 'Your API credentials are invalid. Please check your configuration.';
  }

  if (error instanceof AccessDeniedError) {
    return 'You do not have permission to perform this action. Please contact your administrator.';
  }

  if (error instanceof NotFoundError) {
    return 'The requested resource was not found.';
  }

  if (error instanceof LangfuseAPITimeoutError) {
    return 'The request took too long to complete. Please try again.';
  }

  if (error instanceof LangfuseAPIError) {
    if (error.statusCode === 429) {
      return 'Too many requests. Please wait a moment and try again.';
    }

    if (error.statusCode && error.statusCode >= 500) {
      return 'The Langfuse service is temporarily unavailable. Please try again later.';
    }

    return `An API error occurred (${error.statusCode}). Please try again.`;
  }

  return 'An unexpected error occurred. Please try again.';
}

// Usage in UI
try {
  await client.trace.get('trace-id');
} catch (error) {
  const message = getUserFriendlyMessage(error);
  showErrorToUser(message);
}

Type Definitions

class LangfuseAPIError extends Error {
  readonly statusCode?: number;
  readonly body?: unknown;
  readonly rawResponse?: RawResponse;

  constructor(params: {
    message?: string;
    statusCode?: number;
    body?: unknown;
    rawResponse?: RawResponse;
  });
}

class LangfuseAPITimeoutError extends Error {
  constructor(message: string);
}

class Error extends LangfuseAPIError {}
class UnauthorizedError extends LangfuseAPIError {}
class AccessDeniedError extends LangfuseAPIError {}
class NotFoundError extends LangfuseAPIError {}
class MethodNotAllowedError extends LangfuseAPIError {}

interface RawResponse {
  statusCode: number;
  headers: Record<string, string>;
  body: unknown;
}

Best Practices

  1. Specific Error Handling: Always check for specific error types before handling generic LangfuseAPIError
  2. Timeout Configuration: Set appropriate timeouts based on operation type (longer for batch operations)
  3. Retry Logic: Implement exponential backoff for transient errors (timeouts, 5xx, 429)
  4. Don't Retry 4xx: Never retry client errors (4xx) except for 429 rate limiting
  5. Log Error Details: Always log statusCode and body for debugging
  6. User-Friendly Messages: Transform technical errors into user-friendly messages in UI
  7. Circuit Breaker: Use circuit breaker pattern for external service calls
  8. Monitoring: Send error metrics to monitoring services for alerting
  9. Graceful Degradation: Provide fallback values or cached data when possible
  10. Error Context: Include operation context in error logs (operation name, parameters, duration)

Common HTTP Status Codes

  • 400 Bad Request: Invalid request parameters or body
  • 401 Unauthorized: Missing or invalid API credentials
  • 403 Forbidden: Valid credentials but insufficient permissions
  • 404 Not Found: Resource doesn't exist
  • 405 Method Not Allowed: HTTP method not supported for endpoint
  • 429 Too Many Requests: Rate limit exceeded (implement backoff and retry)
  • 500 Internal Server Error: Server-side error (safe to retry)
  • 502 Bad Gateway: Upstream service error (safe to retry)
  • 503 Service Unavailable: Service temporarily down (safe to retry)
  • 504 Gateway Timeout: Upstream service timeout (safe to retry)

Install with Tessl CLI

npx tessl i tessl/npm-langfuse--core

docs

api-client.md

api-resources.md

constants.md

errors.md

index.md

logger.md

media.md

utils.md

tile.json