Skip to main content

codex_cli_sdk/
errors.rs

1use std::path::PathBuf;
2use thiserror::Error;
3
4/// All errors produced by codex-cli-sdk.
5#[derive(Error, Debug)]
6pub enum Error {
7    #[error(
8        "Codex CLI not found. Install via `npm i -g @openai/codex` or `brew install --cask codex`"
9    )]
10    CliNotFound,
11
12    #[error("Codex CLI at {path} is not executable")]
13    CliNotExecutable { path: PathBuf },
14
15    #[error("Codex CLI version check failed: {0}")]
16    VersionCheck(String),
17
18    #[error("failed to spawn Codex CLI: {0}")]
19    SpawnFailed(#[source] std::io::Error),
20
21    #[error("Codex CLI exited with code {code}: {stderr}")]
22    ProcessExited { code: i32, stderr: String },
23
24    #[error("not connected — call connect() first")]
25    NotConnected,
26
27    #[error("already connected")]
28    AlreadyConnected,
29
30    #[error("transport closed unexpectedly")]
31    TransportClosed,
32
33    #[error("write to stdin failed: {0}")]
34    WriteFailed(#[source] std::io::Error),
35
36    #[error("read from stdout failed: {0}")]
37    ReadFailed(#[source] std::io::Error),
38
39    #[error("JSON parse error: {0}")]
40    Json(#[from] serde_json::Error),
41
42    #[error("unexpected event format: {message}")]
43    UnexpectedEvent { message: String },
44
45    #[error("timeout: {operation}")]
46    Timeout { operation: String },
47
48    #[error("invalid configuration: {0}")]
49    Config(String),
50
51    #[error("approval denied: {0}")]
52    ApprovalDenied(String),
53
54    #[error("concurrent turn in progress — wait for current turn to complete")]
55    ConcurrentTurn,
56
57    /// Thread not found on the CLI side.
58    ///
59    /// TODO: The CLI currently uses unstructured stderr for error reporting.
60    /// Return this variant once the CLI adds structured error codes (e.g. a
61    /// JSON `{"type":"error","code":"thread_not_found"}` event on stdout).
62    #[error("thread not found: {0}")]
63    ThreadNotFound(String),
64
65    #[error("{0}")]
66    Other(String),
67}
68
69/// Convenience alias.
70pub type Result<T> = std::result::Result<T, Error>;
71
72impl Error {
73    /// Whether this error is transient and the operation could be retried.
74    pub fn is_transient(&self) -> bool {
75        matches!(self, Self::Timeout { .. } | Self::TransportClosed)
76    }
77}