# Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd. # SPDX-License-Identifier: AGPL-3.0 """ Unified exception classes for OpenViking. Based on gRPC standard status codes for consistency across service boundaries. """ from typing import Optional class OpenVikingError(Exception): """Base exception for all OpenViking errors.""" def __init__(self, message: str, code: str = "UNKNOWN", details: Optional[dict] = None): super().__init__(message) self.message = message self.code = code self.details = details or {} # ============= Argument Errors ============= class InvalidArgumentError(OpenVikingError): """Invalid argument provided.""" def __init__(self, message: str, details: Optional[dict] = None): super().__init__(message, code="INVALID_ARGUMENT", details=details) class InvalidURIError(InvalidArgumentError): """Invalid Viking URI format.""" def __init__(self, uri: str, reason: str = ""): message = f"Invalid URI: {uri}" if reason: message += f" ({reason})" super().__init__(message, details={"uri": uri, "reason": reason}) self.code = "INVALID_URI" class UnsupportedDirectoryFilesError(InvalidArgumentError): """Directory contains unsupported file types (used when strict=True).""" def __init__(self, message: str, unsupported_files: list): super().__init__(message, details={"unsupported_files": unsupported_files}) self.unsupported_files = unsupported_files # ============= Resource Errors ============= class NotFoundError(OpenVikingError): """Resource not found.""" def __init__(self, resource: str, resource_type: str = "resource"): details = {"type": resource_type} if resource: details["resource"] = resource message = f"{resource_type.capitalize()} not found: {resource}" else: message = f"{resource_type.capitalize()} not found" super().__init__(message, code="NOT_FOUND", details=details) class AlreadyExistsError(OpenVikingError): """Resource already exists.""" def __init__(self, resource: str, resource_type: str = "resource"): message = f"{resource_type.capitalize()} already exists: {resource}" super().__init__( message, code="ALREADY_EXISTS", details={"resource": resource, "type": resource_type} ) class ConflictError(OpenVikingError): """Resource conflict (e.g., locked by another operation).""" def __init__(self, message: str, resource: Optional[str] = None): details = {"resource": resource} if resource else {} super().__init__(message, code="CONFLICT", details=details) class FailedPreconditionError(OpenVikingError): """Operation cannot proceed because a required precondition is unmet.""" def __init__(self, message: str, details: Optional[dict] = None): super().__init__(message, code="FAILED_PRECONDITION", details=details) class AbortedError(OpenVikingError): """Operation was aborted, typically due to a concurrency conflict.""" def __init__(self, message: str = "Operation aborted", details: Optional[dict] = None): super().__init__(message, code="ABORTED", details=details) # ============= Authentication Errors ============= class UnauthenticatedError(OpenVikingError): """Authentication required but not provided or invalid.""" def __init__(self, message: str = "Authentication required"): super().__init__(message, code="UNAUTHENTICATED") class PermissionDeniedError(OpenVikingError): """Permission denied for the requested operation.""" def __init__(self, message: str = "Permission denied", resource: Optional[str] = None): details = {"resource": resource} if resource else {} super().__init__(message, code="PERMISSION_DENIED", details=details) # ============= Service Errors ============= class UnavailableError(OpenVikingError): """Service temporarily unavailable.""" def __init__(self, service: str = "service", reason: str = ""): message = f"{service.capitalize()} unavailable" if reason: message += f": {reason}" super().__init__( message, code="UNAVAILABLE", details={"service": service, "reason": reason} ) class ResourceExhaustedError(OpenVikingError): """Resource quota, rate limit, or billing capacity was exhausted.""" def __init__(self, message: str = "Resource exhausted", details: Optional[dict] = None): super().__init__(message, code="RESOURCE_EXHAUSTED", details=details) class InternalError(OpenVikingError): """Internal server error.""" def __init__(self, message: str = "Internal error", cause: Optional[Exception] = None): details = {"cause": str(cause)} if cause else {} super().__init__(message, code="INTERNAL", details=details) class DeadlineExceededError(OpenVikingError): """Operation timed out.""" def __init__(self, operation: str = "operation", timeout: Optional[float] = None): message = f"{operation.capitalize()} timed out" if timeout: message += f" after {timeout}s" super().__init__( message, code="DEADLINE_EXCEEDED", details={"operation": operation, "timeout": timeout} ) class UnimplementedError(OpenVikingError): """Operation is not implemented.""" def __init__(self, message: str = "Operation not implemented", details: Optional[dict] = None): super().__init__(message, code="UNIMPLEMENTED", details=details) # ============= Business Errors ============= class ProcessingError(OpenVikingError): """Error during resource processing.""" def __init__(self, message: str, source: Optional[str] = None): details = {"source": source} if source else {} super().__init__(message, code="PROCESSING_ERROR", details=details) class EmbeddingFailedError(ProcessingError): """Embedding generation failed.""" def __init__(self, message: str = "Embedding generation failed", source: Optional[str] = None): super().__init__(message, source=source) self.code = "EMBEDDING_FAILED" class VLMFailedError(ProcessingError): """VLM processing failed.""" def __init__(self, message: str = "VLM processing failed", source: Optional[str] = None): super().__init__(message, source=source) self.code = "VLM_FAILED" class SessionExpiredError(OpenVikingError): """Session has expired.""" def __init__(self, session_id: str): message = f"Session expired: {session_id}" super().__init__(message, code="SESSION_EXPIRED", details={"session_id": session_id}) class NotInitializedError(OpenVikingError): """Service or component not initialized.""" def __init__(self, component: str = "service"): message = f"{component.capitalize()} not initialized. Call initialize() first." super().__init__(message, code="NOT_INITIALIZED", details={"component": component})