138890b9d2
- src/ollama-client.ts: Detect HTTP 404 on /api/chat and throw a descriptive ApiError with the model name and the exact ollama pull command needed. - src/error-handler.ts: For API_ERROR type, return the error message directly instead of prefixing with 'API error: ', so the user-friendly 404 message is shown cleanly in the Obsidian notice. - tests/ollama-client.test.ts: Update 404 assertions to match the new descriptive error message.
136 lines
4.5 KiB
TypeScript
136 lines
4.5 KiB
TypeScript
// src/error-handler.ts
|
|
|
|
import { Notice } from 'obsidian';
|
|
import {
|
|
OllamaError,
|
|
ErrorType,
|
|
NetworkError,
|
|
ApiError,
|
|
ValidationError,
|
|
StreamingError,
|
|
ToolExecutionError,
|
|
PathValidationError,
|
|
} from './types';
|
|
|
|
export class ErrorHandler {
|
|
static handleError(error: unknown, context?: string): void {
|
|
const message = this.getUserFriendlyMessage(error);
|
|
new Notice(message);
|
|
|
|
if (error instanceof Error) {
|
|
const ctx = context ? ` [${context}]` : '';
|
|
// Use console.error instead of ErrorHandler.error for fatal errors
|
|
console.error(`Ollama Plugin Error${ctx}: ${error.message}`);
|
|
if (error.stack) {
|
|
console.error(error.stack);
|
|
}
|
|
} else {
|
|
const ctx = context ? ` [${context}]` : '';
|
|
console.error(`Ollama Plugin Error${ctx}:`, error);
|
|
}
|
|
}
|
|
|
|
private static getUserFriendlyMessage(error: unknown): string {
|
|
if (error instanceof OllamaError) {
|
|
return this.getUserFriendlyMessageFromOllamaError(error);
|
|
}
|
|
|
|
if (error instanceof Error) {
|
|
return this.getUserFriendlyMessageFromError(error);
|
|
}
|
|
|
|
return 'An unexpected error occurred';
|
|
}
|
|
|
|
private static getUserFriendlyMessageFromOllamaError(error: OllamaError): string {
|
|
switch (error.type) {
|
|
case ErrorType.NETWORK_ERROR:
|
|
return 'Connection error. Please check if Ollama is running.';
|
|
case ErrorType.API_ERROR:
|
|
return error.message;
|
|
case ErrorType.VALIDATION_ERROR:
|
|
return this.getUserFriendlyValidationMessage(error);
|
|
case ErrorType.STREAMING_ERROR:
|
|
return 'Response too long. Please try a shorter request.';
|
|
case ErrorType.TOOL_EXECUTION_ERROR:
|
|
return `Tool error for ${(error as ToolExecutionError).toolName}. ${error.message}`;
|
|
case ErrorType.PATH_VALIDATION_ERROR:
|
|
return `Invalid file path: ${(error as PathValidationError).path}`;
|
|
case ErrorType.UNKNOWN_ERROR:
|
|
return 'An unexpected error occurred';
|
|
default:
|
|
return 'An unexpected error occurred';
|
|
}
|
|
}
|
|
|
|
private static getUserFriendlyValidationMessage(error: OllamaError): string {
|
|
if (error instanceof ValidationError && error.details?.field) {
|
|
const fieldMsg = error.details.field.charAt(0).toUpperCase() + error.details.field.slice(1);
|
|
return `Invalid ${fieldMsg.toLowerCase()}. ${error.details.message ?? error.message}`;
|
|
}
|
|
return 'Input validation error. Please correct your input.';
|
|
}
|
|
|
|
private static getUserFriendlyMessageFromError(error: Error): string {
|
|
const msg = error.message.toLowerCase();
|
|
|
|
// Check timeout BEFORE network (more specific matches first)
|
|
if (msg.includes('timeout') || msg.includes('timed out') || msg.includes('time out')) {
|
|
return 'Request timed out. Please check your Ollama connection.';
|
|
}
|
|
|
|
if (msg.includes('network') || msg.includes('connection') || msg.includes('fetch')) {
|
|
return 'Connection error. Please check if Ollama is running.';
|
|
}
|
|
|
|
if (msg.includes('validation') || msg.includes('invalid')) {
|
|
return 'Invalid input. Please correct your input.';
|
|
}
|
|
|
|
if (msg.includes('stream') || msg.includes('chunk')) {
|
|
return 'Response too long. Please try a shorter request.';
|
|
}
|
|
|
|
if (msg.includes('tool') || msg.includes('function')) {
|
|
return 'Tool error. Please try again.';
|
|
}
|
|
|
|
if (msg.includes('path') || msg.includes('file')) {
|
|
return 'Invalid file path. Please check the path and try again.';
|
|
}
|
|
|
|
return 'An unexpected error occurred';
|
|
}
|
|
|
|
// -- Factory methods --
|
|
|
|
static createNetworkError(message: string, statusCode?: number): NetworkError {
|
|
return new NetworkError(message, statusCode);
|
|
}
|
|
|
|
static createApiError(message: string, statusCode?: number): ApiError {
|
|
return new ApiError(message, statusCode ?? 500);
|
|
}
|
|
|
|
static createValidationError(message: string, field?: string): ValidationError {
|
|
const details = field ? { field, message } : undefined;
|
|
return new ValidationError(message, details);
|
|
}
|
|
|
|
static createStreamingError(message: string): StreamingError {
|
|
return new StreamingError(message);
|
|
}
|
|
|
|
static createToolExecutionError(message: string, toolName?: string): ToolExecutionError {
|
|
return new ToolExecutionError(message, toolName ?? 'unknown');
|
|
}
|
|
|
|
static createPathValidationError(message: string, path?: string): PathValidationError {
|
|
return new PathValidationError(message, path ?? '');
|
|
}
|
|
|
|
static createUnknownError(message: string): OllamaError {
|
|
return new OllamaError(message, ErrorType.UNKNOWN_ERROR);
|
|
}
|
|
}
|