hyperhive/hive-claude/src/error.rs

85 lines
3.3 KiB
Rust

//! Typed errors for the driver. See the crate-level docs for why this is a
//! `thiserror` enum rather than `anyhow`.
use std::process::ExitStatus;
use thiserror::Error;
/// Why a claude run did not complete cleanly. A normal, finished turn is
/// `Ok(())`; everything else is one of these variants.
///
/// Two families share the enum on purpose, so a caller can handle them with a
/// single `match` on the `Result`:
///
/// - **Recognized sentinels** — [`Error::PromptTooLong`],
/// [`Error::RateLimited`], [`Error::AuthFailed`], [`Error::SessionNotFound`].
/// These are *expected* non-completion states parsed from claude's output,
/// not crashes; callers typically compact, park-and-retry, re-auth, or
/// create-a-session in response.
/// - **Hard failures** — [`Error::Spawn`], [`Error::Stdin`], [`Error::Wait`],
/// [`Error::Exit`], [`Error::Io`]. The process couldn't run, or died with no
/// recognizable reason.
#[derive(Debug, Error)]
pub enum Error {
/// `Prompt is too long` — the session is past the model's context window.
#[error("prompt is too long for the model's context window")]
PromptTooLong,
/// The API refused for rate-limit / usage-cap / credit-balance reasons.
#[error("request was rate-limited or hit a usage/credit cap")]
RateLimited,
/// The API rejected the request with 401 (auth/session expired or revoked).
#[error("authentication failed (HTTP 401)")]
AuthFailed,
/// `--resume` matched no session for the given id or title (e.g. the title
/// was never created, or its backing file was moved away).
#[error("no session matched the requested id or title")]
SessionNotFound,
/// The `claude` binary could not be spawned (not on `PATH`, not
/// executable, …).
#[error("failed to spawn `{program}`: {source}")]
Spawn {
/// The program name we tried to run.
program: String,
/// The underlying spawn error.
#[source]
source: std::io::Error,
},
/// Writing the prompt to claude's stdin failed.
#[error("writing prompt to claude stdin failed: {0}")]
Stdin(#[source] std::io::Error),
/// Awaiting the child process failed.
#[error("waiting on claude failed: {0}")]
Wait(#[source] std::io::Error),
/// The child produced no stdout for longer than the configured idle
/// window (`Config::idle_timeout`) and was killed. Indicative of an
/// Anthropic API stall (e.g. a multi-retry connection storm that goes
/// silent for minutes). Callers typically park briefly and retry, like
/// the rate-limit path.
#[error("claude idle timeout: no output for the configured window")]
IdleTimeout,
/// claude exited non-zero and raised none of the recognized sentinels.
/// `stderr_tail` is the last handful of stderr lines (empty if there were
/// none), included so the caller can surface a real diagnostic.
#[error("claude exited {status}\n{stderr_tail}")]
Exit {
/// The child's exit status.
status: ExitStatus,
/// Tail of stderr, newline-joined; empty when claude wrote nothing.
stderr_tail: String,
},
/// A filesystem operation (session lookup / archive) failed.
#[error("session store i/o failed: {0}")]
Io(#[from] std::io::Error),
}
/// Convenience alias for results from this crate.
pub type Result<T> = std::result::Result<T, Error>;