""" Exception hierarchy for Git Operations MCP Server. Provides structured error handling with error codes for MCP responses. """ from enum import Enum from typing import Any class ErrorCode(str, Enum): """Error codes for Git Operations errors.""" # General errors (1xxx) INTERNAL_ERROR = "GIT_1000" INVALID_REQUEST = "GIT_1001" NOT_FOUND = "GIT_1002" PERMISSION_DENIED = "GIT_1003" TIMEOUT = "GIT_1004" RATE_LIMITED = "GIT_1005" # Workspace errors (2xxx) WORKSPACE_NOT_FOUND = "GIT_2000" WORKSPACE_LOCKED = "GIT_2001" WORKSPACE_SIZE_EXCEEDED = "GIT_2002" WORKSPACE_CREATE_FAILED = "GIT_2003" WORKSPACE_DELETE_FAILED = "GIT_2004" # Git operation errors (3xxx) CLONE_FAILED = "GIT_3000" CHECKOUT_FAILED = "GIT_3001" COMMIT_FAILED = "GIT_3002" PUSH_FAILED = "GIT_3003" PULL_FAILED = "GIT_3004" MERGE_CONFLICT = "GIT_3005" BRANCH_EXISTS = "GIT_3006" BRANCH_NOT_FOUND = "GIT_3007" INVALID_REF = "GIT_3008" DIRTY_WORKSPACE = "GIT_3009" UNCOMMITTED_CHANGES = "GIT_3010" FETCH_FAILED = "GIT_3011" RESET_FAILED = "GIT_3012" # Provider errors (4xxx) PROVIDER_ERROR = "GIT_4000" PROVIDER_AUTH_FAILED = "GIT_4001" PROVIDER_NOT_FOUND = "GIT_4002" PR_CREATE_FAILED = "GIT_4003" PR_MERGE_FAILED = "GIT_4004" PR_NOT_FOUND = "GIT_4005" API_ERROR = "GIT_4006" # Credential errors (5xxx) CREDENTIAL_ERROR = "GIT_5000" CREDENTIAL_NOT_FOUND = "GIT_5001" CREDENTIAL_INVALID = "GIT_5002" SSH_KEY_ERROR = "GIT_5003" class GitOpsError(Exception): """Base exception for Git Operations errors.""" def __init__( self, message: str, code: ErrorCode = ErrorCode.INTERNAL_ERROR, details: dict[str, Any] | None = None, ) -> None: super().__init__(message) self.message = message self.code = code self.details = details or {} def to_dict(self) -> dict[str, Any]: """Convert to dictionary for MCP response.""" result = { "error": self.message, "code": self.code.value, } if self.details: result["details"] = self.details return result # Workspace Errors class WorkspaceError(GitOpsError): """Base exception for workspace-related errors.""" def __init__( self, message: str, code: ErrorCode = ErrorCode.WORKSPACE_NOT_FOUND, details: dict[str, Any] | None = None, ) -> None: super().__init__(message, code, details) class WorkspaceNotFoundError(WorkspaceError): """Workspace does not exist.""" def __init__(self, project_id: str) -> None: super().__init__( f"Workspace not found for project: {project_id}", ErrorCode.WORKSPACE_NOT_FOUND, {"project_id": project_id}, ) class WorkspaceLockedError(WorkspaceError): """Workspace is locked by another operation.""" def __init__(self, project_id: str, holder: str | None = None) -> None: details: dict[str, Any] = {"project_id": project_id} if holder: details["locked_by"] = holder super().__init__( f"Workspace is locked for project: {project_id}", ErrorCode.WORKSPACE_LOCKED, details, ) class WorkspaceSizeExceededError(WorkspaceError): """Workspace size limit exceeded.""" def __init__(self, project_id: str, current_size: float, max_size: float) -> None: super().__init__( f"Workspace size limit exceeded for project: {project_id}", ErrorCode.WORKSPACE_SIZE_EXCEEDED, { "project_id": project_id, "current_size_gb": current_size, "max_size_gb": max_size, }, ) # Git Operation Errors class GitError(GitOpsError): """Base exception for git operation errors.""" def __init__( self, message: str, code: ErrorCode = ErrorCode.INTERNAL_ERROR, details: dict[str, Any] | None = None, ) -> None: super().__init__(message, code, details) class CloneError(GitError): """Failed to clone repository.""" def __init__(self, repo_url: str, reason: str) -> None: super().__init__( f"Failed to clone repository: {reason}", ErrorCode.CLONE_FAILED, {"repo_url": repo_url, "reason": reason}, ) class CheckoutError(GitError): """Failed to checkout branch or ref.""" def __init__(self, ref: str, reason: str) -> None: super().__init__( f"Failed to checkout '{ref}': {reason}", ErrorCode.CHECKOUT_FAILED, {"ref": ref, "reason": reason}, ) class CommitError(GitError): """Failed to commit changes.""" def __init__(self, reason: str) -> None: super().__init__( f"Failed to commit: {reason}", ErrorCode.COMMIT_FAILED, {"reason": reason}, ) class PushError(GitError): """Failed to push to remote.""" def __init__(self, branch: str, reason: str) -> None: super().__init__( f"Failed to push branch '{branch}': {reason}", ErrorCode.PUSH_FAILED, {"branch": branch, "reason": reason}, ) class PullError(GitError): """Failed to pull from remote.""" def __init__(self, branch: str, reason: str) -> None: super().__init__( f"Failed to pull branch '{branch}': {reason}", ErrorCode.PULL_FAILED, {"branch": branch, "reason": reason}, ) class MergeConflictError(GitError): """Merge conflict detected.""" def __init__(self, conflicting_files: list[str]) -> None: super().__init__( f"Merge conflict detected in {len(conflicting_files)} files", ErrorCode.MERGE_CONFLICT, {"conflicting_files": conflicting_files}, ) class BranchExistsError(GitError): """Branch already exists.""" def __init__(self, branch_name: str) -> None: super().__init__( f"Branch already exists: {branch_name}", ErrorCode.BRANCH_EXISTS, {"branch": branch_name}, ) class BranchNotFoundError(GitError): """Branch does not exist.""" def __init__(self, branch_name: str) -> None: super().__init__( f"Branch not found: {branch_name}", ErrorCode.BRANCH_NOT_FOUND, {"branch": branch_name}, ) class InvalidRefError(GitError): """Invalid git reference.""" def __init__(self, ref: str) -> None: super().__init__( f"Invalid git reference: {ref}", ErrorCode.INVALID_REF, {"ref": ref}, ) class DirtyWorkspaceError(GitError): """Workspace has uncommitted changes.""" def __init__(self, modified_files: list[str]) -> None: super().__init__( f"Workspace has {len(modified_files)} uncommitted changes", ErrorCode.DIRTY_WORKSPACE, {"modified_files": modified_files[:10]}, # Limit to first 10 ) # Provider Errors class ProviderError(GitOpsError): """Base exception for provider-related errors.""" def __init__( self, message: str, code: ErrorCode = ErrorCode.PROVIDER_ERROR, details: dict[str, Any] | None = None, ) -> None: super().__init__(message, code, details) class AuthenticationError(ProviderError): """Authentication with provider failed.""" def __init__(self, provider: str, reason: str) -> None: super().__init__( f"Authentication failed with {provider}: {reason}", ErrorCode.PROVIDER_AUTH_FAILED, {"provider": provider, "reason": reason}, ) class ProviderNotFoundError(ProviderError): """Provider not configured or recognized.""" def __init__(self, provider: str) -> None: super().__init__( f"Provider not found or not configured: {provider}", ErrorCode.PROVIDER_NOT_FOUND, {"provider": provider}, ) class PRError(ProviderError): """Pull request operation failed.""" def __init__( self, message: str, code: ErrorCode = ErrorCode.PR_CREATE_FAILED, details: dict[str, Any] | None = None, ) -> None: super().__init__(message, code, details) class PRNotFoundError(PRError): """Pull request not found.""" def __init__(self, pr_number: int, repo: str) -> None: super().__init__( f"Pull request #{pr_number} not found in {repo}", ErrorCode.PR_NOT_FOUND, {"pr_number": pr_number, "repo": repo}, ) class APIError(ProviderError): """Provider API error.""" def __init__( self, provider: str, status_code: int, message: str ) -> None: super().__init__( f"{provider} API error ({status_code}): {message}", ErrorCode.API_ERROR, {"provider": provider, "status_code": status_code, "message": message}, ) # Credential Errors class CredentialError(GitOpsError): """Base exception for credential-related errors.""" def __init__( self, message: str, code: ErrorCode = ErrorCode.CREDENTIAL_ERROR, details: dict[str, Any] | None = None, ) -> None: super().__init__(message, code, details) class CredentialNotFoundError(CredentialError): """Credential not found.""" def __init__(self, credential_type: str, identifier: str) -> None: super().__init__( f"{credential_type} credential not found: {identifier}", ErrorCode.CREDENTIAL_NOT_FOUND, {"type": credential_type, "identifier": identifier}, )