Newer
Older
navi-1 / navi / exceptions.py
class NaviError(Exception):
    """Base exception for all navi errors."""


class SessionNotFound(NaviError):
    def __init__(self, session_id: str):
        super().__init__(f"Session not found: {session_id}")
        self.session_id = session_id


class ProfileNotFound(NaviError):
    def __init__(self, profile_id: str):
        super().__init__(f"Profile not found: {profile_id}")
        self.profile_id = profile_id


class ToolNotFound(NaviError):
    def __init__(self, tool_name: str):
        super().__init__(f"Tool not found: {tool_name}")
        self.tool_name = tool_name


class ToolExecutionError(NaviError):
    def __init__(self, tool_name: str, reason: str):
        super().__init__(f"Tool '{tool_name}' failed: {reason}")
        self.tool_name = tool_name


class LLMBackendError(NaviError):
    pass


class LLMConnectionError(LLMBackendError):
    """Server unreachable — connection refused, timeout, network error."""


class LLMModelNotFoundError(LLMBackendError):
    """Model not found on this server."""


class LLMQuotaExceededError(LLMBackendError):
    """The provider accepted the request and refused it for quota/rate reasons.

    Ollama Cloud answers 429 when the plan's limits are spent for the window.
    It used to arrive as a plain ``LLMBackendError``, which the fallback loop
    did not catch — so hitting the limit ended the turn instead of moving to
    the backup host, the exact case the backup exists for.
    """

    def __init__(self, message: str, retry_after: float | None = None):
        super().__init__(message)
        self.retry_after = retry_after


class LLMAuthError(LLMBackendError):
    """401/403 — the target rejected our credentials.

    Not the same as a bad request: retrying with the same key is pointless,
    but the key can be rotated in the environment, so the target is probed
    again (slowly) rather than disabled for the process lifetime.
    """


class LLMServerUnavailableError(LLMBackendError):
    """5xx from the target — the host is up but cannot serve right now."""


class MaxIterationsReached(NaviError):
    def __init__(self, limit: int):
        super().__init__(f"Agent reached max iterations limit ({limit})")
        self.limit = limit


class ContextTooLargeError(NaviError):
    """Raised when the estimated context size would exceed the model's safe window."""


class NothingToCompactError(NaviError):
    """Raised when a forced /compact has nothing to summarize (context too small)."""