85 lines
3.3 KiB
Rust
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>;
|