X-GPT includes a comprehensive error handling system that provides categorized errors, user-friendly messages, and actionable recovery suggestions.
The error system is located in src/errors/ and consists of four main modules:
src/errors/
types.ts # Error classes and enums
handler.ts # Centralized error handling
messages.ts # User-friendly error messages
api.ts # API-specific error handling
index.ts # Exports
Errors are categorized to enable automatic handling and appropriate recovery suggestions:
| Category | Description | Example |
|---|---|---|
AUTHENTICATION |
Invalid credentials or tokens | Missing OpenAI API key, expired Twitter cookies |
RATE_LIMIT |
Too many requests | Twitter 429 responses, quota exceeded |
NETWORK |
Connection issues | Timeout, DNS failure, connection refused |
API_ERROR |
Downstream API failures | OpenAI API errors, service issues |
DATABASE |
SQLite/Drizzle errors | Table not found, migration failed |
USER_INPUT |
Invalid parameters | Invalid username, bad date format |
COMMAND_USAGE |
CLI usage errors | Missing required arguments, invalid flag combinations |
CONFIGURATION |
Settings issues | Missing .env file, invalid config value |
DATA_VALIDATION |
Data format errors | Invalid JSON, schema mismatch |
FILE_SYSTEM |
File operations | Permission denied, file not found |
SYSTEM |
Runtime errors | Memory, unexpected crashes |
UNKNOWN |
Unclassified errors | Fallback category |
| Severity | Description | Action |
|---|---|---|
LOW |
Warning, operation can continue | Informational |
MEDIUM |
Error, operation failed but recoverable | Retry suggested |
HIGH |
Critical, requires user action | User intervention needed |
FATAL |
System error, application should exit | Immediate attention |
All errors extend from XGPTError:
import { XGPTError, ErrorCategory, ErrorSeverity } from './errors';
const error = new XGPTError({
code: 'CUSTOM_ERROR',
category: ErrorCategory.USER_INPUT,
severity: ErrorSeverity.MEDIUM,
title: 'Custom Error',
message: 'Something went wrong',
technicalDetails: 'Stack trace here...',
context: {
command: 'scrape',
username: 'example'
},
recoveryActions: [
{ description: 'Try again', command: 'xgpt scrape example' }
]
});Pre-configured error classes for common scenarios:
import {
AuthenticationError,
RateLimitError,
NetworkError,
DatabaseError,
ValidationError,
ConfigurationError
} from './errors';
// Authentication error with built-in recovery suggestions
throw new AuthenticationError(
'OpenAI API key is missing',
{ command: 'embed' }
);
// Rate limit error
throw new RateLimitError(
'Twitter rate limit exceeded',
{ operation: 'scraping', username: 'elonmusk' }
);
// Database error
throw new DatabaseError(
'Failed to insert tweet',
{ command: 'scrape' }
);Use handleCommandError for consistent error handling:
import { handleCommandError } from './errors';
export async function scrapeCommand(options: ScrapeOptions): Promise<CommandResult> {
try {
// Command logic...
} catch (error) {
return handleCommandError(error, {
command: 'scrape',
username: options.username,
operation: 'tweet_scraping'
});
}
}The singleton ErrorHandler provides additional functionality:
import { errorHandler, createError, handleWarning } from './errors';
// Initialize with user config (enables verbose logging if configured)
await errorHandler.initialize();
// Create typed errors
const error = createError(
ErrorCategory.AUTHENTICATION,
'Token expired',
{ command: 'scrape' }
);
// Handle non-critical warnings
handleWarning('Using default rate limit profile', { command: 'scrape' });The error handler automatically categorizes errors based on message patterns:
// These are automatically detected:
throw new Error('rate limit exceeded'); // -> RateLimitError
throw new Error('unauthorized'); // -> AuthenticationError
throw new Error('ECONNREFUSED'); // -> NetworkError
throw new Error('sqlite error'); // -> DatabaseError
throw new Error('invalid username format'); // -> ValidationErrorPattern matching rules in handler.ts:
const ERROR_PATTERNS = {
authentication: [/api key/i, /unauthorized/i, /forbidden/i, ...],
rateLimit: [/rate limit/i, /too many requests/i, /429/, ...],
network: [/network/i, /timeout/i, /enotfound/i, ...],
database: [/sqlite/i, /drizzle/i, /migration/i, ...],
validation: [/invalid/i, /required.*parameter/i, ...],
configuration: [/config/i, /\.env/i, ...]
};The messages.ts module provides human-readable error messages for the web UI:
import { toFriendlyError, formatFriendlyErrorHtml } from './errors';
// Convert any error to user-friendly format
const friendly = toFriendlyError(error);
// Returns: { title, message, suggestions, severity }
// Format for web UI
const html = formatFriendlyErrorHtml(friendly);| Technical Error | User-Friendly Title | Message |
|---|---|---|
ECONNREFUSED |
Connection Refused | Could not connect to the server. The service might be down... |
429 |
Rate Limited | You've made too many requests. Twitter limits how often... |
401 |
Authentication Failed | Your credentials are invalid or expired... |
sqlite error |
Database Error | There was an issue with the database... |
For web server routes, use ApiError and factory functions:
import { ApiError, ApiErrors, toApiError, createErrorResponse } from './errors';
// Factory functions for common HTTP errors
throw ApiErrors.badRequest('Username is required');
throw ApiErrors.notFound('User');
throw ApiErrors.unauthorized('Invalid token');
throw ApiErrors.rateLimit('Too many requests');
throw ApiErrors.validation('Invalid date format', 'since');
// Convert any error to ApiError
const apiError = toApiError(unknownError);
// Create standardized response for Elysia
app.post('/api/scrape', async ({ body, set }) => {
try {
// ...
} catch (error) {
return createErrorResponse(error, set);
}
});| Error Category | HTTP Status |
|---|---|
AUTHENTICATION |
401 |
RATE_LIMIT |
429 |
USER_INPUT |
400 |
DATA_VALIDATION |
422 |
DATABASE |
500 |
NETWORK |
503 |
SYSTEM |
500 |
const error = new AuthenticationError('API key missing');
console.error(error.toDisplayFormat());Output:
[error] Authentication Error
API key missing
[tip] Suggested actions:
1. Check your API keys and tokens
Command: xgpt config list
2. Update your OpenAI API key
Command: xgpt config set api.openaiKey <your-key>
console.log(JSON.stringify(error.toJSON(), null, 2));Output:
{
"code": "AUTH_ERROR",
"category": "AUTHENTICATION",
"severity": "HIGH",
"title": "Authentication Error",
"message": "API key missing",
"recoveryActions": [...],
"timestamp": "2024-01-15T10:30:00.000Z"
}-
Always provide context when handling errors:
return handleCommandError(error, { command: 'scrape', username, operation: 'tweet_fetch' });
-
Use specific error classes when throwing:
throw new ValidationError('Username cannot be empty');
-
Chain original errors to preserve stack traces:
throw new DatabaseError('Insert failed', context, originalError);
-
Include recovery actions for actionable errors:
throw new XGPTError({ // ... recoveryActions: [ { description: 'Check config', command: 'xgpt config list' }, { description: 'View docs', url: 'https://...' } ] });
- Command Runner - How commands use error handling
- API Reference - API error responses
- Configuration - Error-related settings