Skip to main content

liminal_server/agents/
error.rs

1//! Named failures that never retain request words or credentials.
2
3/// The words an agent act takes on an exchange with no registry (ACCESS-013 R3).
4pub const NO_REGISTRY: &str =
5    "no agent registry; write [agents] (or [auth.lys.agents] on a Lys exchange)";
6
7/// The words an anonymous open seat's agent act takes (ACCESS-013 R2).
8pub const ANONYMOUS_SEAT: &str = "an anonymous seat has no station; write [open] name";
9
10/// Why an agent operation did nothing, or why its durable outcome is unknown.
11#[derive(Debug, thiserror::Error)]
12pub enum AgentError {
13    /// A deliberate act is still a draft in Lys.
14    #[error("{action} is deliberate: held by draft {draft}")]
15    Held {
16        /// The requested action.
17        action: String,
18        /// The retained draft id.
19        draft: String,
20    },
21    /// A boundary value is malformed; only the member is named.
22    #[error("agent_invalid: {0}")]
23    Invalid(&'static str),
24    /// A name has no retained record.
25    #[error("unknown_agent: {0}")]
26    Unknown(String),
27    /// A subscription names a station no one has placed (ACCESS-013 R3): the
28    /// words say who places one for another name.
29    #[error(
30        "unknown_agent: {0}; a station for another name is placed by a person \
31         (POST /admin/agents/create) or an approved draft"
32    )]
33    NoStation(String),
34    /// A retained name cannot be reused, including after retirement.
35    #[error("agent_name_taken: {0}")]
36    NameTaken(String),
37    /// An identity claim does not match the verified pass.
38    #[error("pass holder {holder} is not agent {agent}")]
39    Identity {
40        /// The verified holder, or unverified.
41        holder: String,
42        /// The claimed agent.
43        agent: String,
44    },
45    /// Another live connection holds this agent's seat. It is never taken
46    /// over or shared: the seat is released when that connection closes, or
47    /// when its peer stops answering the kernel's keepalive.
48    #[error(
49        "agent {agent} is already present on connection {connection}, held since unix ms \
50         {since_ms}; the seat frees when that connection closes or its peer stops answering \
51         keepalive (80 s), so give this client a seat of its own"
52    )]
53    Present {
54        /// The agent's name.
55        agent: String,
56        /// The incumbent connection.
57        connection: u64,
58        /// Epoch milliseconds the incumbent was admitted.
59        since_ms: u64,
60    },
61    /// A configured count or byte bound is reached.
62    #[error("auth.lys.agents.{bound}: limit {limit}, count {count}")]
63    Bound {
64        /// The configuration member.
65        bound: &'static str,
66        /// The configured limit.
67        limit: usize,
68        /// The measured population or byte count.
69        count: usize,
70    },
71    /// The state machine does not admit this event.
72    #[error("agent_transition_refused: {0}")]
73    Transition(&'static str),
74    /// A draft id is already bound to different immutable words.
75    #[error("agent_draft_digest_mismatch")]
76    DraftMismatch,
77    /// Only the server writes presence and state.
78    #[error("agent_subject_server_owned: {0}")]
79    ServerOwned(String),
80    /// A lock or store failed; request bodies are never included.
81    #[error("agent_registry_unavailable: {0}")]
82    Store(String),
83    /// A write may have committed; reopen before making another decision.
84    #[error("agent_registry_outcome_unknown: reopen the store before admission")]
85    Uncertain,
86    /// The library refused delivery, with no body retained in the error.
87    #[error("agent_delivery_failed: {0}")]
88    Delivery(&'static str),
89}