Skip to main content

platonic_core/
error.rs

1//! Error types shared across Platonic Core modules.
2
3/// Core error type for Platonic primitives.
4#[derive(Debug, Eq, PartialEq, thiserror::Error)]
5pub enum Error {
6    /// Identifier constructor received an empty value.
7    #[error("{0} cannot be empty")]
8    EmptyIdentifier(&'static str),
9
10    /// A token budget would be exceeded.
11    #[error("context budget exceeded: used {used}, budget {budget}")]
12    ContextBudgetExceeded {
13        /// Estimated tokens, saturated at `u32::MAX` for reporting.
14        used: u32,
15        /// Maximum tokens allowed by the context pack.
16        budget: u32,
17    },
18
19    /// A context compaction range was empty or reversed.
20    #[error("invalid compaction range: {start}..{end_exclusive}")]
21    InvalidCompactionRange {
22        /// Zero-based start position in the prior-turn list.
23        start: u64,
24        /// Exclusive end position in the prior-turn list.
25        end_exclusive: u64,
26    },
27
28    /// A recorded event sequence number was not the expected next value.
29    #[error("event sequence mismatch: expected {expected}, actual {actual}")]
30    SequenceMismatch {
31        /// Next contiguous sequence number required by the run state.
32        expected: u64,
33        /// Sequence number carried by the rejected record.
34        actual: u64,
35    },
36
37    /// An event for one run was applied to another run.
38    #[error("run id mismatch: expected {expected}, actual {actual}")]
39    RunIdMismatch {
40        /// Run already bound to the state machine.
41        expected: String,
42        /// Run named by the rejected event.
43        actual: String,
44    },
45
46    /// An event was not legal in the current run phase.
47    #[error("invalid transition from {phase} on {event}")]
48    InvalidTransition {
49        /// Stable diagnostic name of the phase that rejected the event.
50        phase: &'static str,
51        /// Stable diagnostic name of the rejected event.
52        event: &'static str,
53    },
54
55    /// A model response did not match the pending model request.
56    #[error("model step mismatch: expected {expected}, actual {actual}")]
57    StepMismatch {
58        /// Step of the pending model request.
59        expected: u32,
60        /// Step carried by the rejected event.
61        actual: u32,
62    },
63
64    /// A turn-scoped event did not match the pending turn.
65    #[error("turn mismatch: expected {expected}, actual {actual}")]
66    TurnMismatch {
67        /// Turn currently awaiting the event.
68        expected: String,
69        /// Turn named by the rejected event.
70        actual: String,
71    },
72
73    /// A new turn reused an earlier turn id from the same run.
74    #[error("turn id was reused: {turn_id}")]
75    TurnReused {
76        /// Reused turn identifier.
77        turn_id: String,
78    },
79
80    /// A whole-batch tool proposal rejection omitted its explanation.
81    #[error("tool proposal rejection reason cannot be empty")]
82    EmptyToolProposalsRejectionReason,
83
84    /// A tool event did not match the pending tool call.
85    #[error("tool call mismatch: expected {expected}, actual {actual}")]
86    ToolCallMismatch {
87        /// Tool call currently awaiting the event.
88        expected: String,
89        /// Tool call named by the rejected event.
90        actual: String,
91    },
92
93    /// A validated tool call reused an earlier call id from the same run.
94    #[error("tool call id was reused: {call_id}")]
95    ToolCallReused {
96        /// Reused tool-call identifier.
97        call_id: String,
98    },
99
100    /// A validated tool call did not match any pending model proposal.
101    #[error("tool call was not proposed by the model")]
102    UnproposedToolCall,
103}