"""Exception hierarchy for the THOX mesh node agent. Errors are split by *who can fix them*, because the agent reacts differently to each class: configuration problems abort startup, transport problems are retried with backoff, and contract rejections are surfaced verbatim so the operator can see exactly which mesh rule was violated. """ from __future__ import annotations class NodeError(Exception): """Base class for every error raised by the node agent.""" class ConfigError(NodeError): """Configuration is missing or self-contradictory. Not retryable.""" class BackendError(NodeError): """The local inference backend failed to start or produce a completion.""" class TunnelError(NodeError): """The public tunnel could not be established or its URL could not be read.""" class MeshTransportError(NodeError): """The mesh could not be reached (DNS, TLS, timeout, 5xx). Retryable.""" class MeshRejectedError(NodeError): """The mesh reached us and refused the request. Not retryable without a change. Carries the backend's own message rather than a paraphrase. The most common instance in this tier is the private-address rule in ``validateLocalBaseUrl``: ``meshstack-device-v2`` rejects any ``base_url`` whose host is not RFC1918, loopback, link-local, CGNAT or ``.local``. A Colab ``trycloudflare.com`` URL and an ``*.hf.space`` URL both fail that check, so the agent must be able to report the refusal precisely instead of retrying forever. """ def __init__(self, message: str, *, status: int | None = None, action: str | None = None) -> None: super().__init__(message) self.status = status self.action = action def __str__(self) -> str: # pragma: no cover - trivial formatting parts = [super().__str__()] if self.action: parts.append(f"action={self.action}") if self.status is not None: parts.append(f"status={self.status}") return " | ".join(parts) class PublicUrlRejectedError(MeshRejectedError): """The mesh refused our public URL because it is not a private address. Raised as a distinct type so the agent can degrade to controller transport instead of treating a known policy boundary as an outage. """