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}