Skip to main content

bamboo_sdk/agent/
error.rs

1//! Typed SDK error surface.
2//!
3//! Replaces the stringly-typed `Result<_, String>` the builder previously
4//! returned (`with_defaults_for_data_dir` / `build`) with a single
5//! `#[derive(thiserror::Error)]` enum, matching the runtime's own [`AgentError`]
6//! (`bamboo_agent_core::AgentError`) instead of forcing callers to match on
7//! substrings. Every fallible SDK entry point added alongside cancellation /
8//! approval+resume / session ergonomics returns `Result<_, SdkError>`.
9//!
10//! `run` / `run_stream` and friends keep returning [`AgentError`] directly (it
11//! was already typed) — [`SdkError::Agent`] lets callers unify the two with `?`
12//! in a function that returns `Result<_, SdkError>`.
13
14use bamboo_agent_core::AgentError;
15use bamboo_engine::session_app::errors::{RespondError, SessionLoadError, SessionSaveError};
16
17/// Errors surfaced by the `bamboo_sdk` facade.
18#[derive(Debug, thiserror::Error)]
19pub enum SdkError {
20    /// A run/resume of the underlying agent loop failed. Wraps the runtime's
21    /// own typed error so `?` composes across `Agent::run*` and the newer
22    /// facade methods in a function returning `Result<_, SdkError>`.
23    #[error(transparent)]
24    Agent(#[from] AgentError),
25
26    /// `AgentBuilder::build()` failed — the wrapped engine builder rejected an
27    /// incomplete or inconsistent dependency set (e.g. a required dependency
28    /// was never supplied).
29    #[error("agent builder error: {0}")]
30    Build(String),
31
32    /// `with_defaults_for_data_dir` failed to read/parse `config.json`.
33    #[error("configuration error: {0}")]
34    Config(String),
35
36    /// `with_defaults_for_data_dir` failed to construct the configured LLM
37    /// provider — commonly a missing/invalid API key. See
38    /// [`AgentBuilder::api_key`](super::AgentBuilder::api_key) and
39    /// [`AgentBuilder::provider_name`](super::AgentBuilder::provider_name).
40    #[error("provider initialization failed: {0}")]
41    ProviderInit(String),
42
43    /// [`AgentBuilder::api_key`](super::AgentBuilder::api_key) was paired with
44    /// a provider that does not accept a plain API key (for example `copilot`,
45    /// which uses cached OAuth) or an unknown provider name.
46    #[error("provider '{provider}' does not accept a plain API key")]
47    UnsupportedApiKeyProvider { provider: String },
48
49    /// A new session was requested but neither `.model(...)` nor the assembled
50    /// provider configuration supplied an effective model.
51    #[error(
52        "no model configured; call AgentBuilder::model or configure the active provider model"
53    )]
54    ModelNotConfigured,
55
56    /// A caller supplied a Project id that is not a valid opaque/path-safe id.
57    #[error("invalid Project id: {0}")]
58    InvalidProjectId(String),
59
60    /// The defaults-backed SDK could not open the first-class Project store.
61    #[error("Project store initialization failed: {0}")]
62    ProjectStoreInit(String),
63
64    /// The configured Project is missing or archived in the defaults-backed store.
65    #[error("Project is unavailable: {0}")]
66    ProjectUnavailable(String),
67
68    /// `with_defaults_for_data_dir` failed to open the session store at the
69    /// given data directory.
70    #[error("session store initialization failed: {0}")]
71    StoreInit(String),
72
73    /// `with_defaults_for_data_dir` failed to initialize the skill manager.
74    #[error("skill manager initialization failed: {0}")]
75    SkillInit(String),
76
77    /// An MCP server failed to start (see
78    /// [`AgentBuilder::mcp_server`](super::AgentBuilder::mcp_server)).
79    #[error("MCP server '{server_id}' failed to start: {source}")]
80    McpServerStart {
81        server_id: String,
82        #[source]
83        source: bamboo_mcp::McpError,
84    },
85
86    /// The requested session does not exist.
87    #[error("session not found: {0}")]
88    SessionNotFound(String),
89
90    /// Loading a session from storage failed.
91    #[error("session load failed: {0}")]
92    SessionLoad(String),
93
94    /// Persisting a session failed.
95    #[error("session save failed: {0}")]
96    SessionSave(String),
97
98    /// [`Agent::answer`](super::Agent::answer) was called on a session with no
99    /// pending question / approval to respond to.
100    #[error("no pending question waiting for response")]
101    NoPendingQuestion,
102
103    /// The response passed to [`Agent::answer`](super::Agent::answer) did not
104    /// match one of the pending question's fixed options (and it does not
105    /// allow a custom response).
106    #[error("invalid response: {0}")]
107    InvalidResponse(String),
108
109    /// A capability that requires `with_defaults_for_data_dir` (e.g. session
110    /// listing, which needs the concrete session-index handle) was called on
111    /// an `Agent` assembled from manually-injected dependencies.
112    #[error("unsupported: {0}")]
113    Unsupported(String),
114
115    /// Underlying I/O failure (session store reads/writes, etc).
116    #[error("I/O error: {0}")]
117    Io(#[from] std::io::Error),
118}
119
120impl From<SessionLoadError> for SdkError {
121    fn from(error: SessionLoadError) -> Self {
122        match error {
123            SessionLoadError::NotFound(id) => SdkError::SessionNotFound(id),
124            SessionLoadError::StorageError(message) => SdkError::SessionLoad(message),
125        }
126    }
127}
128
129impl From<SessionSaveError> for SdkError {
130    fn from(error: SessionSaveError) -> Self {
131        match error {
132            SessionSaveError::StorageError(message) => SdkError::SessionSave(message),
133        }
134    }
135}
136
137impl From<RespondError> for SdkError {
138    fn from(error: RespondError) -> Self {
139        match error {
140            RespondError::NotFound(id) => SdkError::SessionNotFound(id),
141            RespondError::LoadFailed(inner) => inner.into(),
142            RespondError::SaveFailed(inner) => inner.into(),
143            RespondError::NoPendingQuestion => SdkError::NoPendingQuestion,
144            RespondError::InvalidResponse(message) => SdkError::InvalidResponse(message),
145        }
146    }
147}