Overview
Core AI provides a hierarchy of error types that help you handle failures gracefully and provide meaningful feedback to users.
Error Hierarchy
Error
└── LLMError
└── ProviderError
└── StructuredOutputError
├── StructuredOutputNoObjectGeneratedError
├── StructuredOutputParseError
└── StructuredOutputValidationError
LLMError
Base error class for all Core AI errors.
class LLMError extends Error {
public readonly cause?: unknown;
constructor(message: string, cause?: unknown);
}
Usage:
import { generate, LLMError } from '@core-ai/core-ai';
try {
const result = await generate({
model,
messages: [], // Empty messages array
});
} catch (error) {
if (error instanceof LLMError) {
console.error('LLM error:', error.message);
if (error.cause) {
console.error('Caused by:', error.cause);
}
}
}
When thrown:
- Empty messages array
- Empty input for embeddings
- Empty prompt for image generation
- Invalid configuration or parameters
Always check for LLMError first to catch all Core AI errors, then check for more specific error types.
ProviderError
Error specific to provider API failures (network errors, API errors, rate limits, etc.).
class ProviderError extends LLMError {
public readonly provider: string;
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 ProviderError {
public readonly rawOutput?: string;
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 model.generateObject({
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 model.generateObject({
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 model.generateObject({
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 {
generate,
generateObject,
LLMError,
ProviderError,
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 model.generateObject({
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 LLM errors
if (error instanceof LLMError) {
console.error('LLM 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 LLMError('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 LLMError('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