Skip to main content

Overview

core-ai provides a hierarchy of error types that help you handle failures gracefully. For the complete API surface, see the errors reference.

Error Hierarchy

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

CoreAIError

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

  constructor(message: string, cause?: unknown, provider?: string);
}
Usage:
import { CoreAIError, generate } from '@core-ai/core-ai';

try {
  const result = await generate({
    model,
    messages: [], // Empty messages array
  });
} catch (error) {
  if (error instanceof CoreAIError) {
    console.error('core-ai error:', error.message);
    if (error.cause) {
      console.error('Caused by:', error.cause);
    }
  }
}
Check for the most specific error types first, then fall back to CoreAIError to catch any standardized library error.

ValidationError

Thrown when core-ai rejects invalid caller input or local request configuration before making a provider call.
class ValidationError extends CoreAIError {
  constructor(message: string, cause?: unknown, provider?: string);
}
When thrown:
  • Empty messages array
  • Empty input for embeddings
  • Empty prompt for image generation
  • Invalid local request configuration

AbortedError

Thrown when an operation is cancelled via an AbortSignal.
class AbortedError extends CoreAIError {
  constructor(cause?: unknown, provider?: string);
}

StreamAbortedError

Thrown when a streaming operation is aborted via an AbortSignal.
class StreamAbortedError extends AbortedError {
  constructor(cause?: unknown, provider?: string);
}
Usage:
import { stream, 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 essay' }],
    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');
  }
}
StreamAbortedError applies to an in-flight stream. Validation errors such as empty messages throw ValidationError.

ProviderError

Error specific to provider API failures (network errors, API errors, rate limits, etc.).
class ProviderError extends CoreAIError {
  public readonly statusCode?: number;

  constructor(
    message: string,
    provider: string,
    statusCode?: number,
    cause?: unknown
  );
}
Usage:
import { generate, ProviderError } from '@core-ai/core-ai';

try {
  const result = await generate({ model, messages });
} catch (error) {
  if (error instanceof ProviderError) {
    console.error(`Provider: ${error.provider}`);
    console.error(`Status: ${error.statusCode}`);
    console.error(`Message: ${error.message}`);
    
    // Handle specific status codes
    if (error.statusCode === 429) {
      console.log('Rate limited - retry with backoff');
    } else if (error.statusCode === 401) {
      console.log('Authentication failed - check API key');
    }
  }
}
Common Status Codes:
CodeMeaningRecommended Action
401Authentication failedCheck API key
403ForbiddenCheck API permissions
429Rate limit exceededImplement retry with exponential backoff
500Server errorRetry request
503Service unavailableWait and retry
When thrown:
  • Invalid API key
  • Rate limits exceeded
  • Network failures
  • Provider API errors
  • Timeout errors

Structured Output Errors

Errors specific to structured output generation (generateObject and streamObject).

StructuredOutputError

Base class for all structured output errors.
class StructuredOutputError extends CoreAIError {
  public readonly rawOutput?: string;
  public readonly statusCode?: number;

  constructor(
    message: string,
    provider: string,
    options: {
      statusCode?: number;
      cause?: unknown;
      rawOutput?: string;
    }
  );
}
Properties:
  • rawOutput: The raw text output from the model (if available)
  • provider: Which provider was used
  • statusCode: HTTP status code (if applicable)
  • cause: Underlying error that caused this error

StructuredOutputNoObjectGeneratedError

Thrown when the model doesn’t generate any structured output.
import { generateObject, StructuredOutputNoObjectGeneratedError } from '@core-ai/core-ai';
import { z } from 'zod';

const schema = z.object({
  name: z.string(),
  age: z.number(),
});

try {
  const result = await generateObject({
    model,
    messages: [{ role: 'user', content: 'Hello' }],
    schema,
  });
} catch (error) {
  if (error instanceof StructuredOutputNoObjectGeneratedError) {
    console.error('Model did not generate structured output');
    console.error('Raw output:', error.rawOutput);
  }
}
When thrown:
  • Model responds with regular text instead of structured output
  • Tool call for structured output was not made
  • Finish reason is not ‘stop’ or ‘tool-calls’

StructuredOutputParseError

Thrown when the model’s output cannot be parsed as JSON.
import { generateObject, StructuredOutputParseError } from '@core-ai/core-ai';

try {
  const result = await generateObject({
    model,
    messages: [{ role: 'user', content: 'Generate user data' }],
    schema,
  });
} catch (error) {
  if (error instanceof StructuredOutputParseError) {
    console.error('Failed to parse JSON output');
    console.error('Raw output:', error.rawOutput);
    console.error('Parse error:', error.cause);
  }
}
When thrown:
  • Model output is not valid JSON
  • JSON syntax errors in model output
  • Malformed structured output

StructuredOutputValidationError

Thrown when the model’s output doesn’t match the Zod schema.
import { generateObject, StructuredOutputValidationError } from '@core-ai/core-ai';

const schema = z.object({
  name: z.string().min(1),
  age: z.number().int().positive(),
  email: z.string().email(),
});

try {
  const result = await generateObject({
    model,
    messages: [{ role: 'user', content: 'Generate user data' }],
    schema,
  });
} catch (error) {
  if (error instanceof StructuredOutputValidationError) {
    console.error('Schema validation failed');
    console.error('Issues:', error.issues);
    console.error('Raw output:', error.rawOutput);
    
    // Display each validation issue
    error.issues.forEach((issue, i) => {
      console.log(`${i + 1}. ${issue}`);
    });
  }
}
Properties:
  • issues: Array of human-readable validation error messages
When thrown:
  • Missing required fields
  • Wrong data types
  • Values that don’t match schema constraints
  • Invalid enum values

Complete Error Handling Example

import {
  CoreAIError,
  generate,
  generateObject,
  ProviderError,
  StructuredOutputError,
  StructuredOutputNoObjectGeneratedError,
  StructuredOutputParseError,
  StructuredOutputValidationError,
} from '@core-ai/core-ai';
import { z } from 'zod';

async function generateUserProfile(prompt: string) {
  const schema = z.object({
    name: z.string(),
    age: z.number(),
    email: z.string().email(),
  });

  try {
    const result = await generateObject({
      model,
      messages: [{ role: 'user', content: prompt }],
      schema,
      schemaName: 'UserProfile',
    });
    
    return result.object;
  } catch (error) {
    // Handle validation errors - most specific
    if (error instanceof StructuredOutputValidationError) {
      console.error('Schema validation failed:');
      error.issues.forEach(issue => console.error(`- ${issue}`));
      throw new Error('Invalid user profile format');
    }
    
    // Handle parse errors
    if (error instanceof StructuredOutputParseError) {
      console.error('Failed to parse JSON output');
      console.error('Raw output:', error.rawOutput);
      throw new Error('Model returned invalid JSON');
    }
    
    // Handle missing output
    if (error instanceof StructuredOutputNoObjectGeneratedError) {
      console.error('Model did not generate structured output');
      throw new Error('Model did not follow instructions');
    }
    
    // Handle provider errors
    if (error instanceof ProviderError) {
      if (error.statusCode === 429) {
        console.log('Rate limited - implementing retry...');
        await new Promise(resolve => setTimeout(resolve, 5000));
        return generateUserProfile(prompt); // Retry
      }
      
      console.error(`Provider error (${error.provider}):`, error.message);
      throw new Error('AI service unavailable');
    }
    
    // Handle generic core-ai errors
    if (error instanceof CoreAIError) {
      console.error('core-ai error:', error.message);
      throw new Error('AI request failed');
    }
    
    // Handle unexpected errors
    console.error('Unexpected error:', error);
    throw error;
  }
}

Retry Strategies

Exponential Backoff

async function generateWithRetry(
  model: ChatModel,
  messages: Message[],
  maxRetries = 3
) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await generate({ model, messages });
    } catch (error) {
      if (error instanceof ProviderError && error.statusCode === 429) {
        const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
        console.log(`Rate limited. Retrying in ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      throw error; // Don't retry other errors
    }
  }
  throw new Error('Max retries exceeded');
}

Circuit Breaker

class CircuitBreaker {
  private failures = 0;
  private lastFailureTime = 0;
  private readonly threshold = 5;
  private readonly timeout = 60000; // 1 minute

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.isOpen()) {
      throw new Error('Circuit breaker is open');
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  private isOpen(): boolean {
    if (this.failures >= this.threshold) {
      const timeSinceLastFailure = Date.now() - this.lastFailureTime;
      return timeSinceLastFailure < this.timeout;
    }
    return false;
  }

  private onSuccess() {
    this.failures = 0;
  }

  private onFailure() {
    this.failures++;
    this.lastFailureTime = Date.now();
  }
}

const breaker = new CircuitBreaker();

const result = await breaker.execute(() =>
  generate({ model, messages })
);

Validation Best Practices

Validate inputs before sending: Check for empty messages, validate file sizes, and ensure proper content types before making API calls to avoid unnecessary errors.
function validateMessages(messages: Message[]): void {
  if (messages.length === 0) {
    throw new ValidationError('messages must not be empty');
  }
  
  for (const message of messages) {
    if (message.role === 'user' && typeof message.content === 'string') {
      if (message.content.trim().length === 0) {
        throw new ValidationError('user message content must not be empty');
      }
    }
  }
}

Logging Errors

Log error details for debugging: Include provider, model, status codes, and raw output when available to help diagnose issues.
import { ProviderError } from '@core-ai/core-ai';

function logError(error: unknown, context: Record<string, unknown>) {
  const errorInfo: Record<string, unknown> = {
    ...context,
    message: error instanceof Error ? error.message : String(error),
    name: error instanceof Error ? error.name : 'Unknown',
  };

  if (error instanceof ProviderError) {
    errorInfo.provider = error.provider;
    errorInfo.statusCode = error.statusCode;
  }

  if (error instanceof StructuredOutputError) {
    errorInfo.rawOutput = error.rawOutput;
  }

  if (error instanceof StructuredOutputValidationError) {
    errorInfo.validationIssues = error.issues;
  }

  console.error('AI Error:', JSON.stringify(errorInfo, null, 2));
}

// Usage
try {
  const result = await generate({ model, messages });
} catch (error) {
  logError(error, {
    operation: 'generate',
    model: model.modelId,
    provider: model.provider,
    messageCount: messages.length,
  });
  throw error;
}

Next Steps

  • Learn about Providers for provider-specific behavior
  • Explore Configuration for controlling generation
  • Understand Messages for building conversations