"""客户端错误类型定义。 定义 ``ClientError`` 抽象基类及其 4 个具体子类,表示由调用方引起的错误 (4xx 语义):参数校验失败、认证失败、资源不存在、权限不足。 """ from __future__ import annotations from typing import Any from yuxi.channels.contract.errors.base import Error class ClientError(Error): """客户端错误抽象基类。 表示由调用方引起的错误(4xx 语义)。子类包括 ``ValidationError`` / ``AuthError`` / ``NotFoundError`` / ``PermissionDeniedError``。 """ error_code = "CLIENT_ERROR" class ValidationError(ClientError, ValueError): """字段校验失败错误。 同时继承 ValueError 以兼容 Pydantic field_validator。 """ error_code = "VALIDATION_ERROR" def __init__( self, field: str, message: str, *, trace_id: str | None = None, ) -> None: super().__init__(message, trace_id=trace_id) self.field = field def to_dict(self) -> dict[str, Any]: data = super().to_dict() data["field"] = self.field return data class AuthError(ClientError): """认证失败错误。 调用方身份认证失败时抛出,如 token 无效、签名错误(HTTP 401)。 """ error_code = "AUTH_ERROR" def __init__( self, reason: str, *, trace_id: str | None = None, ) -> None: super().__init__(f"Authentication failed: {reason}", trace_id=trace_id) self.reason = reason def to_dict(self) -> dict[str, Any]: data = super().to_dict() data["reason"] = self.reason return data class NotFoundError(ClientError): """资源不存在错误。 调用方请求的资源不存在时抛出(HTTP 404)。 """ error_code = "NOT_FOUND" def __init__( self, resource: str, id: str, *, trace_id: str | None = None, ) -> None: super().__init__(f"{resource} not found: {id}", trace_id=trace_id) self.resource = resource self.id = id def to_dict(self) -> dict[str, Any]: data = super().to_dict() data["resource"] = self.resource data["id"] = self.id return data class PermissionDeniedError(ClientError): """权限不足错误。 调用方无权限执行请求的操作时抛出(HTTP 403)。命名为 ``PermissionDeniedError`` 以避免与 Python 内置 ``PermissionError`` 冲突。 """ error_code = "PERMISSION_DENIED" def __init__( self, reason: str, *, trace_id: str | None = None, ) -> None: super().__init__(f"Permission denied: {reason}", trace_id=trace_id) self.reason = reason def to_dict(self) -> dict[str, Any]: data = super().to_dict() data["reason"] = self.reason return data