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}