""" Centralized error handling framework for the journal organizer application. Provides consistent error types and handling patterns across all components. """ import logging import traceback from dataclasses import dataclass, field from datetime import datetime from typing import Dict, Any, Optional, Union, List class JournalOrganizerError(Exception): """Base exception for journal organizer errors""" def __init__( self, message: str, context: Optional[Dict[str, Any]] = None, cause: Optional[Exception] = None, ) -> None: """ Initialize base error Args: message: Error message context: Additional context information cause: Original exception that caused this error """ super().__init__(message) self.message = message self.context = context or {} self.cause = cause self.timestamp = datetime.now().isoformat() def to_dict(self) -> Dict[str, Any]: """Convert error to dictionary for logging/serialization""" return { "error_type": self.__class__.__name__, "message": self.message, "context": self.context, "timestamp": self.timestamp, "cause": str(self.cause) if self.cause else None, } class ConfigurationError(JournalOrganizerError): """Raised when configuration is invalid or missing""" def __init__( self, message: str, config_key: Optional[str] = None, config_value: Optional[Any] = None, cause: Optional[Exception] = None, ) -> None: context = {} if config_key: context["config_key"] = config_key if config_value is not None: # Sanitize sensitive values if ( "key" in str(config_key).lower() or "password" in str(config_key).lower() ): context["config_value"] = "[REDACTED]" else: context["config_value"] = str(config_value) super().__init__(message, context, cause) class ClaudeConfigurationError(ConfigurationError): """Specific error for Claude API configuration issues""" def __init__( self, message: str, config_key: Optional[str] = None, config_value: Optional[Any] = None, suggestions: Optional[list] = None, corrective_action: Optional[str] = None, cause: Optional[Exception] = None, ) -> None: # Add Claude-specific context context = {} if config_key: context["config_key"] = config_key if config_value is not None: # Sanitize sensitive values for Claude config if any(sensitive in str(config_key).lower() for sensitive in ["key", "token", "secret"]): context["config_value"] = "[REDACTED]" else: context["config_value"] = str(config_value) if suggestions: context["suggestions"] = suggestions if corrective_action: context["corrective_action"] = corrective_action super().__init__(message, config_key, config_value, cause) self.suggestions = suggestions or [] self.corrective_action = corrective_action def get_user_friendly_message(self) -> str: """Get a user-friendly error message with suggestions""" message_parts = [self.message] if self.suggestions: message_parts.append("\nSuggestions:") for suggestion in self.suggestions: message_parts.append(f" • {suggestion}") if self.corrective_action: message_parts.append(f"\nTo fix this: {self.corrective_action}") return "\n".join(message_parts) class ClaudeAPIURLError(ClaudeConfigurationError): """Error for invalid Claude API URL configuration""" def __init__( self, message: str, invalid_url: Optional[str] = None, cause: Optional[Exception] = None, ) -> None: suggestions = [ "Use a valid HTTP or HTTPS URL (e.g., https://api.anthropic.com)", "Ensure the URL doesn't have trailing slashes", "For custom endpoints, verify the server is running and accessible", "For localhost endpoints, use http://localhost:PORT or https://localhost:PORT" ] corrective_action = ( "Update your configuration with a valid API URL. " "The default Anthropic API URL is 'https://api.anthropic.com'" ) super().__init__( message=message, config_key="api_url", config_value=invalid_url, suggestions=suggestions, corrective_action=corrective_action, cause=cause ) class ClaudeModelValidationError(ClaudeConfigurationError): """Error for invalid Claude model names with suggestions""" def __init__( self, message: str, invalid_model: Optional[str] = None, valid_models: Optional[list] = None, cause: Optional[Exception] = None, ) -> None: # Default valid models if not provided if not valid_models: valid_models = [ "claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022", "claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307" ] suggestions = [ f"Use one of these supported models: {', '.join(valid_models[:3])}", "Model names should follow pattern: claude-X-Y-YYYYMMDD or claude-X-Y-latest", "Check the Anthropic documentation for the latest available models", "Ensure the model name is spelled correctly and includes the date suffix" ] corrective_action = ( f"Update your model configuration to use a valid model name. " f"Recommended: '{valid_models[0]}'" ) super().__init__( message=message, config_key="model", config_value=invalid_model, suggestions=suggestions, corrective_action=corrective_action, cause=cause ) class ClaudeAPIKeyError(ClaudeConfigurationError): """Error for invalid Claude API key configuration""" def __init__( self, message: str, cause: Optional[Exception] = None, ) -> None: suggestions = [ "Obtain a valid API key from https://console.anthropic.com/", "Ensure the API key starts with 'sk-ant-'", "Set the API key as an environment variable: export ANTHROPIC_API_KEY='sk-ant-...'", "Verify the API key is not truncated or contains extra whitespace", "Check that your API key has the necessary permissions" ] corrective_action = ( "Get a valid Claude API key from the Anthropic console and set it in your configuration " "or as the ANTHROPIC_API_KEY environment variable" ) super().__init__( message=message, config_key="api_key", config_value="[REDACTED]", suggestions=suggestions, corrective_action=corrective_action, cause=cause ) class ClaudeConnectionError(ClaudeConfigurationError): """Error for Claude API connection issues""" def __init__( self, message: str, api_url: Optional[str] = None, status_code: Optional[int] = None, cause: Optional[Exception] = None, ) -> None: suggestions = [] corrective_action = "" # Provide specific suggestions based on the error context if status_code == 401: suggestions = [ "Verify your API key is correct and active", "Check that the API key has not expired", "Ensure you're using the correct API key for the endpoint" ] corrective_action = "Update your API key with a valid, active key from the Anthropic console" elif status_code == 404: suggestions = [ "Verify the API URL is correct", "Check if the API endpoint is available", "For custom endpoints, ensure the service is running" ] corrective_action = "Verify and correct your API URL configuration" elif status_code == 429: suggestions = [ "You've exceeded the API rate limit", "Wait before making more requests", "Consider implementing request throttling" ] corrective_action = "Wait for the rate limit to reset before retrying" elif "connection" in message.lower() or "timeout" in message.lower(): suggestions = [ "Check your internet connection", "Verify the API URL is accessible", "For custom endpoints, ensure the server is running", "Check firewall settings that might block the connection" ] corrective_action = "Verify network connectivity and API endpoint availability" elif "ssl" in message.lower() or "certificate" in message.lower(): suggestions = [ "For localhost endpoints, SSL errors are expected", "For custom HTTPS endpoints, ensure valid SSL certificates", "Consider using HTTP for local development endpoints" ] corrective_action = "Check SSL certificate configuration or use HTTP for local endpoints" else: suggestions = [ "Check the API endpoint status", "Verify your configuration is correct", "Try again after a short delay" ] corrective_action = "Review your Claude API configuration and try again" context = {} if api_url: context["api_url"] = api_url if status_code: context["status_code"] = status_code super().__init__( message=message, config_key="connection", suggestions=suggestions, corrective_action=corrective_action, cause=cause ) # Add additional context self.context.update(context) class APIError(JournalOrganizerError): """Raised when external API calls fail""" def __init__( self, message: str, api_name: Optional[str] = None, status_code: Optional[int] = None, response_data: Optional[str] = None, cause: Optional[Exception] = None, ) -> None: context = {} if api_name: context["api_name"] = api_name if status_code: context["status_code"] = status_code if response_data: # Limit response data length to prevent log spam context["response_data"] = ( f"{response_data[:500]}..." if len(response_data) > 500 else response_data ) super().__init__(message, context, cause) class ValidationError(JournalOrganizerError): """Raised when input validation fails""" def __init__( self, message: str, field_name: Optional[str] = None, field_value: Optional[Any] = None, validation_rule: Optional[str] = None, cause: Optional[Exception] = None, ) -> None: context = {} if field_name: context["field_name"] = field_name if field_value is not None: # Sanitize potentially sensitive field values if any( sensitive in str(field_name).lower() for sensitive in ["password", "key", "token", "secret"] ): context["field_value"] = "[REDACTED]" else: context["field_value"] = str(field_value)[:100] # Limit length if validation_rule: context["validation_rule"] = validation_rule super().__init__(message, context, cause) class FileSystemError(JournalOrganizerError): """Raised when file system operations fail""" def __init__( self, message: str, file_path: Optional[str] = None, operation: Optional[str] = None, cause: Optional[Exception] = None, ) -> None: context = {} if file_path: context["file_path"] = file_path if operation: context["operation"] = operation super().__init__(message, context, cause) class SecurityError(JournalOrganizerError): """Raised when security violations are detected""" def __init__( self, message: str, security_issue: Optional[str] = None, attempted_path: Optional[str] = None, risk_level: str = "high", cause: Optional[Exception] = None, ) -> None: context = {} if security_issue: context["security_issue"] = security_issue if attempted_path: # Sanitize the attempted path to prevent log injection context["attempted_path"] = str(attempted_path)[:500] # Limit length context["risk_level"] = risk_level super().__init__(message, context, cause) @dataclass class ErrorContext: """Context information for error handling""" component: str operation: str user_message: str = "" technical_details: Dict[str, Any] = field(default_factory=dict) severity: str = "error" # error, warning, info def __post_init__(self) -> None: """Validate error context after initialization""" if not self.component or not self.component.strip(): raise ValueError("component cannot be empty") if not self.operation or not self.operation.strip(): raise ValueError("operation cannot be empty") valid_severities = ["error", "warning", "info"] if self.severity not in valid_severities: raise ValueError(f"severity must be one of {valid_severities}") if not isinstance(self.technical_details, dict): raise ValueError("technical_details must be a dictionary") def to_dict(self) -> Dict[str, Any]: """Convert to dictionary""" return { "component": self.component, "operation": self.operation, "user_message": self.user_message, "technical_details": self.technical_details, "severity": self.severity, } class ErrorHandler: """Centralized error handling and logging""" def __init__(self, logger: Optional[logging.Logger] = None) -> None: """ Initialize error handler Args: logger: Logger instance to use (creates default if None) """ self.logger = logger or logging.getLogger(__name__) def handle_error(self, error: Exception, context: ErrorContext) -> Dict[str, Any]: """ Handle any error with consistent logging and formatting Args: error: The exception that occurred context: Error context information Returns: Standardized error response dictionary """ # Log the error with appropriate level log_level = getattr(logging, context.severity.upper(), logging.ERROR) if isinstance(error, JournalOrganizerError): # Use our custom error information error_dict = error.to_dict() # Sanitize the error dictionary sanitized_error_dict = self._sanitize_context_data(error_dict) self.logger.log( log_level, f"{context.component}.{context.operation}: {error.message}", extra={"error_context": sanitized_error_dict}, ) else: # Handle unexpected errors sanitized_context = self._sanitize_context_data(context.to_dict()) self.logger.log( log_level, f"{context.component}.{context.operation}: {str(error)}", extra={ "error_context": sanitized_context, "traceback": traceback.format_exc(), }, ) # Return standardized error response with sanitized messages return { "success": False, "error": self._sanitize_error_message(str(error)), "message": context.user_message or f"Error in {context.operation}", "component": context.component, "operation": context.operation, "timestamp": datetime.now().isoformat(), "error_type": error.__class__.__name__, } def handle_api_error( self, error: Exception, api_name: str, operation: str ) -> Dict[str, Any]: """ Handle API-related errors consistently Args: error: The exception that occurred api_name: Name of the API (e.g., 'obsidian', 'claude') operation: Operation being performed Returns: Standardized error response """ if isinstance(error, APIError): api_error = error else: # Wrap generic exceptions in APIError api_error = APIError( message=f"{api_name} API error: {str(error)}", api_name=api_name, cause=error, ) context = ErrorContext( component=f"{api_name}_api", operation=operation, user_message=f"Failed to communicate with {api_name}. Please check your configuration and try again.", severity="error", ) return self.handle_error(api_error, context) def handle_validation_error( self, error: Exception, field_name: str, operation: str ) -> Dict[str, Any]: """ Handle validation errors with user-friendly messages Args: error: The validation exception field_name: Name of the field that failed validation operation: Operation being performed Returns: Standardized error response """ if isinstance(error, ValidationError): validation_error = error else: validation_error = ValidationError( message=f"Invalid {field_name}: {str(error)}", field_name=field_name, cause=error, ) context = ErrorContext( component="validation", operation=operation, user_message=f"Please check your {field_name} and try again.", severity="warning", ) return self.handle_error(validation_error, context) def handle_configuration_error( self, error: Exception, config_key: str ) -> Dict[str, Any]: """ Handle configuration errors with helpful guidance Args: error: The configuration exception config_key: Configuration key that caused the error Returns: Standardized error response """ if isinstance(error, ConfigurationError): config_error = error else: config_error = ConfigurationError( message=f"Configuration error for {config_key}: {str(error)}", config_key=config_key, cause=error, ) # Provide helpful guidance based on config key guidance_messages = { "api_key": "Please check your API key configuration. Ensure the key is valid and properly set.", "vault_path": "Please verify that the Obsidian vault path exists and is accessible.", "url": "Please check the API URL configuration. Ensure the service is running and accessible.", "model": "Please verify the AI model name is correct and available.", } user_message = guidance_messages.get( config_key, f"Please check your {config_key} configuration." ) context = ErrorContext( component="configuration", operation="load_config", user_message=user_message, severity="error", ) return self.handle_error(config_error, context) def handle_claude_configuration_error( self, error: Exception, config_key: str = None, operation: str = "validate_config" ) -> Dict[str, Any]: """ Handle Claude-specific configuration errors with detailed guidance Args: error: The configuration exception config_key: Specific Claude configuration key that caused the error operation: Operation being performed when error occurred Returns: Standardized error response with Claude-specific guidance """ # Convert to Claude-specific error if not already if isinstance(error, ClaudeConfigurationError): claude_error = error else: # Create appropriate Claude error based on config key if config_key == "api_url": claude_error = ClaudeAPIURLError( message=str(error), cause=error ) elif config_key == "model": claude_error = ClaudeModelValidationError( message=str(error), cause=error ) elif config_key == "api_key": claude_error = ClaudeAPIKeyError( message=str(error), cause=error ) else: claude_error = ClaudeConfigurationError( message=f"Claude configuration error for {config_key}: {str(error)}", config_key=config_key, cause=error ) # Get user-friendly message with suggestions user_message = claude_error.get_user_friendly_message() context = ErrorContext( component="claude_configuration", operation=operation, user_message=user_message, severity="error", ) return self.handle_error(claude_error, context) def handle_claude_connection_error( self, error: Exception, api_url: str = None, operation: str = "test_connection" ) -> Dict[str, Any]: """ Handle Claude API connection errors with specific guidance Args: error: The connection exception api_url: API URL that failed to connect operation: Operation being performed when error occurred Returns: Standardized error response with connection-specific guidance """ # Extract status code if available status_code = None error_str = str(error) # Try to extract HTTP status codes from common error messages import re status_match = re.search(r'\b(4\d{2}|5\d{2})\b', error_str) if status_match: status_code = int(status_match.group(1)) # Create Claude connection error if isinstance(error, ClaudeConnectionError): connection_error = error else: connection_error = ClaudeConnectionError( message=str(error), api_url=api_url, status_code=status_code, cause=error ) # Get user-friendly message with suggestions user_message = connection_error.get_user_friendly_message() context = ErrorContext( component="claude_api", operation=operation, user_message=user_message, severity="error", ) return self.handle_error(connection_error, context) def _sanitize_error_message(self, message: str) -> str: """ Sanitize error messages to prevent sensitive information exposure Args: message: Original error message Returns: Sanitized error message """ # List of patterns that might contain sensitive information sensitive_patterns = [ "api_key", "password", "token", "secret", "auth", "bearer", "key", "pwd", "pass", "credential", "authorization", ] sanitized = message # Replace potential sensitive values with placeholder import re # Pattern 1: key=value, key:value patterns for pattern in sensitive_patterns: patterns_to_replace = [ # Match key=value patterns (with optional quotes) rf'{pattern}["\']?\s*[:=]\s*["\']?[^\s"\'&,;]+["\']?', # Match Bearer token patterns rf"Bearer\s+[^\s]+", # Match quoted key-value pairs rf'["\']?{pattern}["\']?\s*:\s*["\'][^"\']+["\']', # Match URL parameters rf"{pattern}=[^&\s]+", # Match JSON-like patterns rf'["\']?{pattern}["\']?\s*:\s*["\']?[^"\'&,;\s]+["\']?', ] for regex_pattern in patterns_to_replace: sanitized = re.sub( regex_pattern, f"{pattern}=[REDACTED]", sanitized, flags=re.IGNORECASE, ) # Additional patterns for common sensitive data additional_patterns = [ # Email addresses ( r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b", "[EMAIL_REDACTED]", ), # IP addresses (be conservative, only redact private ranges) ( r"\b(?:10\.|172\.(?:1[6-9]|2[0-9]|3[01])\.|192\.168\.)\d{1,3}\.\d{1,3}\b", "[IP_REDACTED]", ), # File paths that might contain usernames (r"/Users/[^/\s]+", "/Users/[USER_REDACTED]"), (r"C:\\\\Users\\\\[^\\\\s]+", "C:\\\\Users\\\\[USER_REDACTED]"), # UUIDs (might be sensitive identifiers) ( r"\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b", "[UUID_REDACTED]", ), # Long hex strings that might be tokens/keys (r"\b[0-9a-fA-F]{32,}\b", "[HEX_TOKEN_REDACTED]"), ] for pattern, replacement in additional_patterns: sanitized = re.sub(pattern, replacement, sanitized) return sanitized def _sanitize_context_data(self, data: Dict[str, Any]) -> Dict[str, Any]: """ Sanitize context data to prevent sensitive information exposure Args: data: Original context data Returns: Sanitized context data """ if not isinstance(data, dict): return data sanitized = {} sensitive_keys = { "api_key", "password", "token", "secret", "auth", "bearer", "key", "pwd", "pass", "credential", "authorization", "private_key", "client_secret", "access_token", "refresh_token", } for key, value in data.items(): key_lower = str(key).lower() # Check if key contains sensitive information if any(sensitive in key_lower for sensitive in sensitive_keys): sanitized[key] = "[REDACTED]" elif isinstance(value, dict): # Recursively sanitize nested dictionaries sanitized[key] = self._sanitize_context_data(value) elif isinstance(value, str): # Sanitize string values sanitized[key] = self._sanitize_error_message(value) else: sanitized[key] = value return sanitized # Global error handler instance _global_error_handler: Optional[ErrorHandler] = None def get_error_handler() -> ErrorHandler: """Get the global error handler instance""" global _global_error_handler if _global_error_handler is None: _global_error_handler = ErrorHandler() return _global_error_handler def set_error_handler(handler: ErrorHandler) -> None: """Set the global error handler instance""" global _global_error_handler _global_error_handler = handler def audit_error_message_security(message: str) -> Dict[str, Any]: """ Audit an error message for potential security issues Args: message: Error message to audit Returns: Dictionary containing audit results """ issues = [] # Check for potential sensitive patterns sensitive_patterns = [ (r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b", "email_address"), (r"\b(?:\d{1,3}\.){3}\d{1,3}\b", "ip_address"), (r"/Users/[^/\s]+", "user_path"), (r"C:\\Users\\[^\\s]+", "windows_user_path"), (r"\b[0-9a-fA-F]{32,}\b", "potential_token"), ( r"(?i)(api_key|password|token|secret|auth|bearer)\s*[:=]\s*[^\s]+", "credential_pattern", ), (r"Bearer\s+[^\s]+", "bearer_token"), ] for pattern, issue_type in sensitive_patterns: import re if re.search(pattern, message): issues.append( { "type": issue_type, "pattern": pattern, "severity": "high" if "token" in issue_type or "credential" in issue_type else "medium", } ) return { "message": message, "has_issues": len(issues) > 0, "issues": issues, "risk_level": "high" if any(issue["severity"] == "high" for issue in issues) else "medium" if issues else "low", } def transform_pydantic_validation_error(validation_error, config_section: str = "configuration") -> ClaudeConfigurationError: """ Transform Pydantic validation errors into user-friendly Claude configuration errors Args: validation_error: Pydantic ValidationError instance config_section: Configuration section being validated Returns: ClaudeConfigurationError with user-friendly message and suggestions """ try: # Import pydantic ValidationError if available from pydantic import ValidationError as PydanticValidationError if not isinstance(validation_error, PydanticValidationError): # Not a pydantic error, create generic error return ClaudeConfigurationError( message=f"Configuration validation failed: {str(validation_error)}", cause=validation_error ) errors = validation_error.errors() if not errors: return ClaudeConfigurationError( message="Unknown validation error occurred", cause=validation_error ) # Process the first error (most relevant) first_error = errors[0] field_path = " -> ".join(str(loc) for loc in first_error.get('loc', [])) error_msg = first_error.get('msg', 'Unknown validation error') error_type = first_error.get('type', 'unknown') # Determine the specific field that failed field_name = first_error.get('loc', [])[-1] if first_error.get('loc') else 'unknown' # Create specific error based on field if field_name == 'api_url': return ClaudeAPIURLError( message=f"Invalid API URL: {error_msg}", invalid_url=first_error.get('input'), cause=validation_error ) elif field_name == 'model': return ClaudeModelValidationError( message=f"Invalid model name: {error_msg}", invalid_model=first_error.get('input'), cause=validation_error ) elif field_name == 'api_key': return ClaudeAPIKeyError( message=f"Invalid API key: {error_msg}", cause=validation_error ) else: # Generic Claude configuration error suggestions = [] corrective_action = "" # Add specific suggestions based on error type if error_type == 'value_error': suggestions.append("Check that the value meets the required format") corrective_action = f"Update the {field_name} field with a valid value" elif error_type == 'missing': suggestions.append(f"The {field_name} field is required") corrective_action = f"Add the missing {field_name} field to your configuration" elif error_type == 'type_error': suggestions.append(f"The {field_name} field has the wrong data type") corrective_action = f"Ensure {field_name} is the correct data type" return ClaudeConfigurationError( message=f"Configuration error in {field_path}: {error_msg}", config_key=field_name, suggestions=suggestions, corrective_action=corrective_action, cause=validation_error ) except ImportError: # Pydantic not available, create generic error return ClaudeConfigurationError( message=f"Configuration validation failed: {str(validation_error)}", cause=validation_error ) def create_claude_configuration_help_message() -> str: """ Create a comprehensive help message for Claude configuration Returns: Formatted help message with configuration guidance """ return """ Claude API Configuration Help: 1. API Key (api_key): - Required: Yes - Format: Must start with 'sk-ant-' - Source: Get from https://console.anthropic.com/ - Environment Variable: ANTHROPIC_API_KEY - Example: sk-ant-api03-abc123... 2. API URL (api_url): - Required: No (defaults to https://api.anthropic.com) - Format: Valid HTTP/HTTPS URL - Custom Endpoints: Supported for proxy servers or regional endpoints - Example: https://api.anthropic.com 3. Model (model): - Required: No (defaults to claude-3-5-sonnet-20241022) - Format: claude-X-Y-YYYYMMDD or claude-X-Y-latest - Supported Models: • claude-3-5-sonnet-20241022 (recommended) • claude-3-5-haiku-20241022 • claude-3-opus-20240229 • claude-3-sonnet-20240229 • claude-3-haiku-20240307 4. Additional Parameters: - max_tokens: Maximum response length (1-200000, default: 4096) - temperature: Response randomness (0.0-1.0, default: 0.7) 5. Environment Variables: - Use ${VARIABLE_NAME} syntax in configuration - Set required variables: export ANTHROPIC_API_KEY="your-key" - Default values: ${VARIABLE_NAME:-default_value} 6. Common Issues: - API Key: Ensure it's complete and starts with 'sk-ant-' - URL: Use valid HTTP/HTTPS format without trailing slashes - Model: Check spelling and include date suffix - Connection: Verify network access and endpoint availability 7. Testing Configuration: - The system will validate your configuration on startup - Connection tests are performed automatically - Check logs for detailed error information For more help, visit: https://docs.anthropic.com/claude/reference/ """ def get_claude_error_recovery_suggestions(error_type: str, context: Dict[str, Any] = None) -> List[str]: """ Get specific recovery suggestions based on error type and context Args: error_type: Type of error (e.g., 'connection', 'authentication', 'model') context: Additional context information Returns: List of recovery suggestions """ context = context or {} suggestions = [] if error_type == 'connection': suggestions = [ "Check your internet connection", "Verify the API URL is correct and accessible", "For custom endpoints, ensure the service is running", "Check firewall settings that might block API access", "Try again after a short delay" ] if context.get('api_url') and 'localhost' in context['api_url']: suggestions.insert(2, "For localhost endpoints, ensure the local service is running") elif error_type == 'authentication': suggestions = [ "Verify your API key is correct and complete", "Check that the API key starts with 'sk-ant-'", "Ensure the API key hasn't expired", "Verify the API key has necessary permissions", "Get a new API key from https://console.anthropic.com/" ] elif error_type == 'model': suggestions = [ "Check the model name spelling and format", "Ensure the model name includes the date suffix (YYYYMMDD)", "Use a supported model like 'claude-3-5-sonnet-20241022'", "Verify the model is available in your region", "Check the Anthropic documentation for available models" ] if context.get('invalid_model'): suggestions.insert(0, f"Replace '{context['invalid_model']}' with a valid model name") elif error_type == 'rate_limit': suggestions = [ "Wait before making more API requests", "Implement request throttling in your application", "Consider upgrading your API plan for higher limits", "Reduce the frequency of API calls", "Use exponential backoff for retries" ] elif error_type == 'validation': suggestions = [ "Check your configuration file syntax", "Ensure all required fields are present", "Verify data types match the expected format", "Remove any extra or invalid configuration fields", "Use the configuration example as a reference" ] else: # Generic suggestions suggestions = [ "Check your Claude API configuration", "Verify all required fields are present and correct", "Review the error message for specific details", "Consult the configuration documentation", "Try restarting the application after fixing the configuration" ] return suggestions