zeph_subagent/error.rs
1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4/// All errors that can arise during sub-agent lifecycle operations.
5///
6/// [`SubAgentError`] is the single error type for the entire `zeph-subagent` crate.
7/// Every fallible public function returns `Result<_, SubAgentError>`.
8///
9/// # Examples
10///
11/// ```rust
12/// use zeph_subagent::{SubAgentDef, SubAgentError};
13///
14/// let err = SubAgentDef::parse("missing frontmatter").unwrap_err();
15/// assert!(matches!(err, SubAgentError::Parse { .. }));
16/// ```
17#[derive(Debug, thiserror::Error)]
18#[non_exhaustive]
19pub enum SubAgentError {
20 /// Frontmatter parsing failed (malformed YAML/TOML or missing delimiters).
21 #[error("parse error in {path}: {reason}")]
22 Parse { path: String, reason: String },
23
24 /// Definition semantics are invalid (e.g. empty name, conflicting tool policies).
25 #[error("invalid definition: {0}")]
26 Invalid(String),
27
28 /// No definition or running agent with the requested name or ID was found.
29 #[error("agent not found: {0}")]
30 NotFound(String),
31
32 /// The background task could not be spawned (OS or tokio error).
33 #[error("spawn failed: {0}")]
34 Spawn(String),
35
36 /// The manager's concurrency limit is exhausted; no new agents can be spawned.
37 #[error("concurrency limit reached (active: {active}, max: {max})")]
38 ConcurrencyLimit { active: usize, max: usize },
39
40 /// The agent loop was cancelled via its [`tokio_util::sync::CancellationToken`].
41 #[error("cancelled")]
42 Cancelled,
43
44 /// A slash-command string (`/agent`, `/agents`) could not be parsed.
45 #[error("invalid command: {0}")]
46 InvalidCommand(String),
47
48 /// An I/O operation on a transcript file failed.
49 #[error("transcript error: {0}")]
50 Transcript(String),
51
52 /// A transcript's hash chain failed to verify (issue #6360): a definite tamper verdict, a
53 /// partial strip of chain metadata, an unverifiable/possibly-re-keyed chain, or a chained
54 /// file read with no history-integrity key configured. Distinct from [`SubAgentError::Transcript`]
55 /// (JSON-syntax/I-O errors) because a chain break always escalates to a hard failure — even
56 /// in `TranscriptReader::load`'s otherwise-lenient mode — since it invalidates trust in
57 /// everything downstream of the break, unlike a single malformed line.
58 #[error("{0}")]
59 Integrity(String),
60
61 /// An ID prefix matched more than one transcript; provide a longer prefix.
62 #[error("ambiguous id prefix '{0}': matches {1} agents")]
63 AmbiguousId(String, usize),
64
65 /// Resume was requested for an agent that is still running.
66 #[error("agent '{0}' is still running; cancel it first or wait for completion")]
67 StillRunning(String),
68
69 /// A memory directory could not be created or resolved.
70 #[error("memory error for agent '{name}': {reason}")]
71 Memory { name: String, reason: String },
72
73 /// A filesystem I/O error unrelated to transcripts.
74 #[error("I/O error at {path}: {reason}")]
75 Io { path: String, reason: String },
76
77 /// The underlying LLM provider returned an error during the agent loop.
78 #[error("LLM call failed: {0}")]
79 Llm(String),
80
81 /// A channel send (status watch, secret approval) failed.
82 #[error("channel send failed: {0}")]
83 Channel(String),
84
85 /// The tokio task panicked and the join handle propagated the panic.
86 #[error("task panicked: {0}")]
87 TaskPanic(String),
88
89 /// The recursion depth for nested sub-agent spawning exceeded the configured limit.
90 #[error("max spawn depth exceeded (depth: {depth}, max: {max})")]
91 MaxDepthExceeded { depth: u32, max: u32 },
92
93 /// Worktree creation or cwd setup failed during agent spawn.
94 ///
95 /// This error is returned when `permissions.worktree = true` and the worktree
96 /// manager fails to create a dedicated worktree or cannot restore the working
97 /// directory. The agent loop never starts in this case (INV-4).
98 #[error("worktree setup failed: {0}")]
99 WorktreeSetup(String),
100
101 /// The durable promise layer returned an error during subagent spawn or await.
102 ///
103 /// Wraps a [`zeph_durable::DurableError`] string so the crate does not take a hard
104 /// compile-time dependency on `zeph-durable` in code paths where the feature is disabled
105 /// at runtime (the `durable` module is always compiled in but the adapter functions are
106 /// only called when `durable.enabled && durable.subagent`).
107 #[error("durable error: {0}")]
108 Durable(String),
109}