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 CoreAIError and provide structured error information.

Error Hierarchy

CoreAIError
├── ValidationError
├── AbortedError
│   └── StreamAbortedError
├── ProviderError
└── StructuredOutputError
    ├── StructuredOutputNoObjectGeneratedError
    ├── StructuredOutputParseError
    └── StructuredOutputValidationError

CoreAIError

Base error class for all core-ai errors.
export class CoreAIError extends Error {
    public readonly provider?: string;
    public readonly cause?: unknown;

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

ValidationError

Thrown when core-ai rejects invalid caller input or local request configuration before calling a provider.
export class ValidationError extends CoreAIError {
    constructor(message: string, cause?: unknown, provider?: string) {
        super(message, cause, provider);
        this.name = 'ValidationError';
    }
}

Properties

message
string
Error message describing what went wrong.
cause
unknown
Optional underlying cause of the error (e.g., network error, API error).
provider
string | undefined
Optional provider context when the error can be attributed to a provider.
name
string
Error name, always 'CoreAIError'.

Example

import { CoreAIError } from '@core-ai/core-ai';

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

AbortedError

Thrown when an operation is cancelled via an AbortSignal.
export class AbortedError extends CoreAIError {
    constructor(cause?: unknown, provider?: string) {
        super('operation aborted', cause, provider);
        this.name = 'AbortedError';
    }
}

StreamAbortedError

Thrown when a streaming operation is aborted via an AbortSignal.
export class StreamAbortedError extends AbortedError {
    constructor(cause?: unknown, provider?: string) {
        super(cause, provider);
        this.name = 'StreamAbortedError';
        this.message = 'stream aborted';
    }
}

Example

import { stream } from '@core-ai/core-ai';
import { StreamAbortedError } from '@core-ai/core-ai';

const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);

try {
  const chatStream = await stream({
    model,
    messages: [{ role: 'user', content: 'Write a long story' }],
    signal: controller.signal,
  });

  for await (const event of chatStream) {
    if (event.type === 'text-delta') {
      process.stdout.write(event.text);
    }
  }
} catch (error) {
  if (error instanceof StreamAbortedError) {
    console.log('Stream was cancelled');
  }
}

ProviderError

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

    constructor(
        message: string,
        provider: string,
        statusCode?: number,
        cause?: unknown
    ) {
        super(message, cause, provider);
        this.name = 'ProviderError';
        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 { generate, ProviderError } from '@core-ai/core-ai';
import { createOpenAI } from '@core-ai/openai';

const openai = createOpenAI();
const model = openai.chatModel('gpt-5-mini');

try {
  const result = await generate({
    model,
    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 CoreAIError {
    public readonly statusCode?: number;
    public readonly rawOutput?: string;

    constructor(
        message: string,
        provider: string,
        options: StructuredOutputErrorOptions = {}
    ) {
        super(message, options.cause, provider);
        this.name = 'StructuredOutputError';
        this.statusCode = options.statusCode;
        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 '@core-ai/core-ai';

try {
  const result = await generateObject({
    model,
    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 '@core-ai/core-ai';

try {
  const result = await generateObject({
    model,
    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 '@core-ai/core-ai';

try {
  const result = await generateObject({
    model,
    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 '@core-ai/core-ai';
import { z } from 'zod';

try {
  const result = await generateObject({
    model,
    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 {
  CoreAIError,
  ProviderError,
  StructuredOutputError,
  StructuredOutputNoObjectGeneratedError,
  StructuredOutputParseError,
  StructuredOutputValidationError
} from '@core-ai/core-ai';

try {
  const result = await generateObject({
    model,
    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 core-ai errors
  if (error instanceof CoreAIError) {
    console.error('core-ai error:', error.message);
    return handleCoreAIError(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,
    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 CoreAIError) {
    console.error({
      type: 'core_ai_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