Skip to main content

Overview

Core AI provides a hierarchy of error classes for handling different types of failures when working with language models. All errors extend from LLMError and provide structured error information.

Error Hierarchy

LLMError
└── ProviderError
    └── StructuredOutputError
        ├── StructuredOutputNoObjectGeneratedError
        ├── StructuredOutputParseError
        └── StructuredOutputValidationError

LLMError

Base error class for all Core AI errors.
export class LLMError extends Error {
    public readonly cause?: unknown;

    constructor(message: string, cause?: unknown) {
        super(message);
        this.name = 'LLMError';
        this.cause = cause;
    }
}

Properties

message
string
Error message describing what went wrong.
cause
unknown
Optional underlying cause of the error (e.g., network error, API error).
name
string
Error name, always 'LLMError'.

Example

import { LLMError } from '@coreai/core';

try {
  // Some operation
  throw new LLMError('Something went wrong');
} catch (error) {
  if (error instanceof LLMError) {
    console.error('LLM Error:', error.message);
    if (error.cause) {
      console.error('Caused by:', error.cause);
    }
  }
}

ProviderError

Error specific to provider/API failures.
export class ProviderError extends LLMError {
    public readonly provider: string;
    public readonly statusCode?: number;

    constructor(
        message: string,
        provider: string,
        statusCode?: number,
        cause?: unknown
    ) {
        super(message, cause);
        this.name = 'ProviderError';
        this.provider = provider;
        this.statusCode = statusCode;
    }
}

Properties

provider
string
The provider that threw the error (e.g., 'openai', 'anthropic').
statusCode
number | undefined
HTTP status code if available (e.g., 404, 500, 429).

Example

import { ProviderError } from '@coreai/core';
import { generate } from '@coreai/core';
import { openai } from '@coreai/openai';

try {
  const result = await generate({
    model: openai('gpt-4'),
    messages: [{ role: 'user', content: 'Hello' }]
  });
} catch (error) {
  if (error instanceof ProviderError) {
    console.error(`Provider ${error.provider} error:`, error.message);
    
    if (error.statusCode === 429) {
      console.error('Rate limit exceeded');
    } else if (error.statusCode === 401) {
      console.error('Authentication failed');
    } else if (error.statusCode && error.statusCode >= 500) {
      console.error('Provider server error');
    }
  }
}

StructuredOutputError

Base class for errors related to structured output generation (objects).
export class StructuredOutputError extends ProviderError {
    public readonly rawOutput?: string;

    constructor(
        message: string,
        provider: string,
        options: StructuredOutputErrorOptions = {}
    ) {
        super(message, provider, options.statusCode, options.cause);
        this.name = 'StructuredOutputError';
        this.rawOutput = options.rawOutput;
    }
}

type StructuredOutputErrorOptions = {
    statusCode?: number;
    cause?: unknown;
    rawOutput?: string;
};

Properties

rawOutput
string | undefined
The raw output from the model that failed to parse/validate.

Example

import { StructuredOutputError } from '@coreai/core';

try {
  const result = await generateObject({
    model: openai('gpt-4'),
    messages: [{ role: 'user', content: 'Generate data' }],
    schema: z.object({ name: z.string() })
  });
} catch (error) {
  if (error instanceof StructuredOutputError) {
    console.error('Structured output error:', error.message);
    console.error('Provider:', error.provider);
    
    if (error.rawOutput) {
      console.error('Raw output:', error.rawOutput);
    }
  }
}

StructuredOutputNoObjectGeneratedError

Thrown when the model doesn’t generate an object at all.
export class StructuredOutputNoObjectGeneratedError extends StructuredOutputError {
    constructor(
        message: string,
        provider: string,
        options: StructuredOutputErrorOptions = {}
    ) {
        super(message, provider, options);
        this.name = 'StructuredOutputNoObjectGeneratedError';
    }
}

Example

import { StructuredOutputNoObjectGeneratedError } from '@coreai/core';

try {
  const result = await generateObject({
    model: openai('gpt-4'),
    messages: [{ role: 'user', content: 'Hello' }],
    schema: z.object({ name: z.string() })
  });
} catch (error) {
  if (error instanceof StructuredOutputNoObjectGeneratedError) {
    console.error('Model did not generate an object');
    console.error('Provider:', error.provider);
  }
}

StructuredOutputParseError

Thrown when the model’s output cannot be parsed as JSON.
export class StructuredOutputParseError extends StructuredOutputError {
    constructor(
        message: string,
        provider: string,
        options: StructuredOutputErrorOptions = {}
    ) {
        super(message, provider, options);
        this.name = 'StructuredOutputParseError';
    }
}

Example

import { StructuredOutputParseError } from '@coreai/core';

try {
  const result = await generateObject({
    model: openai('gpt-4'),
    messages: [{ role: 'user', content: 'Generate user' }],
    schema: z.object({ name: z.string() })
  });
} catch (error) {
  if (error instanceof StructuredOutputParseError) {
    console.error('Failed to parse model output as JSON');
    console.error('Raw output:', error.rawOutput);
    console.error('Parse error:', error.cause);
  }
}

StructuredOutputValidationError

Thrown when the model’s output doesn’t match the Zod schema.
export class StructuredOutputValidationError extends StructuredOutputError {
    public readonly issues: string[];

    constructor(
        message: string,
        provider: string,
        issues: string[],
        options: StructuredOutputErrorOptions = {}
    ) {
        super(message, provider, options);
        this.name = 'StructuredOutputValidationError';
        this.issues = issues;
    }
}

Properties

issues
string[]
Array of validation error messages from Zod.

Example

import { StructuredOutputValidationError } from '@coreai/core';
import { z } from 'zod';

try {
  const result = await generateObject({
    model: openai('gpt-4'),
    messages: [{ role: 'user', content: 'Generate user' }],
    schema: z.object({
      name: z.string(),
      age: z.number().min(0).max(120),
      email: z.string().email()
    })
  });
} catch (error) {
  if (error instanceof StructuredOutputValidationError) {
    console.error('Schema validation failed');
    console.error('Issues:');
    error.issues.forEach(issue => {
      console.error(`  - ${issue}`);
    });
    console.error('Raw output:', error.rawOutput);
  }
}

Error Handling Patterns

Comprehensive Error Handling

import {
  LLMError,
  ProviderError,
  StructuredOutputError,
  StructuredOutputNoObjectGeneratedError,
  StructuredOutputParseError,
  StructuredOutputValidationError
} from '@coreai/core';

try {
  const result = await generateObject({
    model: openai('gpt-4'),
    messages: [{ role: 'user', content: 'Generate data' }],
    schema: z.object({ name: z.string() })
  });
} catch (error) {
  // Handle specific errors first
  if (error instanceof StructuredOutputValidationError) {
    console.error('Validation failed:');
    error.issues.forEach(issue => console.error(issue));
    return handleValidationError(error);
  }
  
  if (error instanceof StructuredOutputParseError) {
    console.error('Parse error:', error.rawOutput);
    return handleParseError(error);
  }
  
  if (error instanceof StructuredOutputNoObjectGeneratedError) {
    console.error('No object generated');
    return handleNoObjectError(error);
  }
  
  // Handle provider errors
  if (error instanceof ProviderError) {
    if (error.statusCode === 429) {
      return handleRateLimit(error);
    }
    if (error.statusCode && error.statusCode >= 500) {
      return handleServerError(error);
    }
    return handleProviderError(error);
  }
  
  // Handle general LLM errors
  if (error instanceof LLMError) {
    console.error('LLM error:', error.message);
    return handleLLMError(error);
  }
  
  // Unknown error
  throw error;
}

Retry Logic with Error Handling

async function generateWithRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3
): Promise<T> {
  let lastError: Error;
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error as Error;
      
      if (error instanceof ProviderError) {
        // Don't retry client errors (4xx)
        if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
          throw error;
        }
        
        // Retry server errors (5xx) and rate limits
        if (error.statusCode === 429 || (error.statusCode && error.statusCode >= 500)) {
          const delay = Math.pow(2, i) * 1000; // Exponential backoff
          console.log(`Retrying in ${delay}ms...`);
          await new Promise(resolve => setTimeout(resolve, delay));
          continue;
        }
      }
      
      // Don't retry validation errors
      if (error instanceof StructuredOutputValidationError) {
        throw error;
      }
      
      // Retry other errors
      if (i < maxRetries - 1) {
        console.log(`Attempt ${i + 1} failed, retrying...`);
      }
    }
  }
  
  throw lastError!;
}

// Usage
const result = await generateWithRetry(() => 
  generate({
    model: openai('gpt-4'),
    messages: [{ role: 'user', content: 'Hello' }]
  })
);

Logging and Monitoring

function logError(error: unknown): void {
  if (error instanceof StructuredOutputValidationError) {
    console.error({
      type: 'validation_error',
      provider: error.provider,
      issues: error.issues,
      rawOutput: error.rawOutput
    });
  } else if (error instanceof StructuredOutputParseError) {
    console.error({
      type: 'parse_error',
      provider: error.provider,
      rawOutput: error.rawOutput
    });
  } else if (error instanceof ProviderError) {
    console.error({
      type: 'provider_error',
      provider: error.provider,
      statusCode: error.statusCode,
      message: error.message
    });
  } else if (error instanceof LLMError) {
    console.error({
      type: 'llm_error',
      message: error.message,
      cause: error.cause
    });
  }
}

Graceful Degradation

async function generateWithFallback(
  primaryModel: ChatModel,
  fallbackModel: ChatModel,
  messages: Message[]
): Promise<GenerateResult> {
  try {
    return await generate({
      model: primaryModel,
      messages
    });
  } catch (error) {
    if (error instanceof ProviderError) {
      console.warn('Primary model failed, using fallback');
      
      try {
        return await generate({
          model: fallbackModel,
          messages
        });
      } catch (fallbackError) {
        console.error('Fallback also failed');
        throw fallbackError;
      }
    }
    
    throw error;
  }
}

Best Practices

Always check for specific error types before general ones. Use instanceof checks in order from most specific to least specific.
Don’t retry validation errors (StructuredOutputValidationError) - they indicate a schema mismatch that won’t be fixed by retrying.
Use exponential backoff for retrying rate limit errors (429) and server errors (5xx).
The rawOutput property in structured output errors is useful for debugging what the model actually generated.
Log error details including provider, status codes, and causes for debugging and monitoring.

Common HTTP Status Codes

  • 400 - Bad Request (invalid parameters)
  • 401 - Unauthorized (invalid API key)
  • 403 - Forbidden (insufficient permissions)
  • 404 - Not Found (invalid model or endpoint)
  • 429 - Too Many Requests (rate limit exceeded)
  • 500 - Internal Server Error
  • 502 - Bad Gateway
  • 503 - Service Unavailable

Source Location

~/workspace/source/packages/core-ai/src/errors.ts