"""Error types that map one-to-one onto ``hux.error.v1`` records.""" from __future__ import annotations class HuxError(Exception): """Base class; ``status`` and ``code`` follow identity.schema.json#/$defs/error.""" status = 500 code = "invalid" def __init__(self, message: str, details: list[str] | None = None) -> None: super().__init__(message) self.message = message self.details = list(details or []) def record(self) -> dict: """Serialise as a ``hux.error.v1`` record.""" body = {"schema": "hux.error.v1", "status": self.status, "code": self.code, "message": self.message[:280]} if self.details: body["details"] = [d[:280] for d in self.details[:32]] return body class Unauthorized(HuxError): """Identity headers missing, malformed or not trusted.""" status, code = 401, "unauthorized" class Forbidden(HuxError): """Identity is valid but does not own the resource.""" status, code = 403, "forbidden" class NotFound(HuxError): """Resource does not exist for this tenant.""" status, code = 404, "not_found" class FlagOff(HuxError): """Card (or one of its dependencies) is disabled; indistinguishable from not found on purpose.""" status, code = 404, "flag_off" class Conflict(HuxError): """If-Match revision mismatch or duplicate create.""" status, code = 409, "conflict" class Invalid(HuxError): """Body failed contract validation.""" status, code = 400, "invalid" class Unprocessable(HuxError): """Well-formed body that violates a rule (hash mismatch, policy violation).""" status, code = 422, "unprocessable" class TooLarge(HuxError): """Body, record or family exceeds its bound.""" status, code = 413, "too_large" class RateLimited(HuxError): """The caller exceeded the bounded per-subject request rate.""" status, code = 429, "rate_limited" def __init__(self, retry_after: int) -> None: super().__init__("request rate limit exceeded") self.retry_after = max(1, int(retry_after)) class ApprovalRequired(HuxError): """An action needs an approval record before it may proceed.""" status, code = 403, "approval_required" class BudgetExhausted(HuxError): """A run budget has been spent.""" status, code = 429, "budget_exhausted"