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:
| Code | Meaning | Recommended Action |
|---|
| 401 | Authentication failed | Check API key |
| 403 | Forbidden | Check API permissions |
| 429 | Rate limit exceeded | Implement retry with exponential backoff |
| 500 | Server error | Retry request |
| 503 | Service unavailable | Wait 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