Skip to main content

contextdb_core/
error.rs

1use crate::types::{TxId, VectorIndexRef};
2
3#[derive(Debug, thiserror::Error)]
4pub enum Error {
5    #[error("table not found: {0}")]
6    TableNotFound(String),
7    #[error("{0} are immutable")]
8    ImmutableTable(String),
9    #[error("column `{column}` on table `{table}` is immutable")]
10    ImmutableColumn { table: String, column: String },
11    #[error("invalid state transition: {0}")]
12    InvalidStateTransition(String),
13    #[error("propagation aborted: {table}.{column} transition {from} -> {to} is invalid")]
14    PropagationAborted {
15        table: String,
16        column: String,
17        from: String,
18        to: String,
19    },
20    #[error("BFS depth exceeds maximum allowed ({0})")]
21    BfsDepthExceeded(u32),
22    #[error("BFS visited set exceeded limit ({0})")]
23    BfsVisitedExceeded(usize),
24    #[error("vector index dimension mismatch on {index:?}: expected {expected}, got {actual}")]
25    VectorIndexDimensionMismatch {
26        index: VectorIndexRef,
27        expected: usize,
28        actual: usize,
29    },
30    #[error("unknown vector index {index:?}")]
31    UnknownVectorIndex { index: VectorIndexRef },
32    #[error("rank policy on index `{index}` references unknown column `{column}`")]
33    RankPolicyColumnUnknown { index: String, column: String },
34    #[error(
35        "rank policy on index `{index}` references ambiguous column `{column}` (present on both anchor and joined table); rename one of the columns -- the rank-formula grammar does not support table-qualified column references"
36    )]
37    RankPolicyColumnAmbiguous { index: String, column: String },
38    #[error(
39        "rank policy on index `{index}` references column `{column}` of type {actual}; expected {expected}"
40    )]
41    RankPolicyColumnType {
42        index: String,
43        column: String,
44        expected: String,
45        actual: String,
46    },
47    #[error("rank policy on index `{index}` joins unknown table `{table}`")]
48    RankPolicyJoinTableUnknown { index: String, table: String },
49    #[error(
50        "rank policy on index `{index}` joins column `{column}` not present on table `{table}`"
51    )]
52    RankPolicyJoinColumnUnknown {
53        index: String,
54        table: String,
55        column: String,
56    },
57    #[error(
58        "rank policy on index `{index}` joins column `{joined_table}.{column}` which has no index; add: CREATE INDEX {joined_table}_{column}_idx ON {joined_table}({column});"
59    )]
60    RankPolicyJoinColumnUnindexed {
61        index: String,
62        joined_table: String,
63        column: String,
64    },
65    #[error("rank policy sort key `{sort_key}` not found on index `{index}`")]
66    RankPolicyNotFound { index: String, sort_key: String },
67    #[error(
68        "rank policy formula on index `{index}` failed to parse at position {position}: {reason}"
69    )]
70    RankPolicyFormulaParse {
71        index: String,
72        position: usize,
73        reason: String,
74    },
75    #[error("USE RANK requires ORDER BY embedding <=> $param in the same query")]
76    UseRankRequiresVectorOrder,
77    #[error("USE RANK requires LIMIT in the same query")]
78    UseRankRequiresLimit,
79    #[error(
80        "{}",
81        drop_blocked_rank_policy_display(
82            table,
83            column,
84            dropped_index,
85            policy_table,
86            policy_column,
87            sort_key
88        )
89    )]
90    DropBlockedByRankPolicy {
91        table: Box<str>,
92        column: Option<Box<str>>,
93        dropped_index: Option<Box<str>>,
94        policy_table: Box<str>,
95        policy_column: Box<str>,
96        sort_key: Box<str>,
97    },
98    #[error(
99        "legacy vector store detected (format marker {found_format_marker:?}); rebuild required for release {expected_release} - sync from a 1.0+ peer or recreate the schema and reimport"
100    )]
101    LegacyVectorStoreDetected {
102        found_format_marker: String,
103        expected_release: String,
104    },
105    #[error("corrupt vector store at {path}: {reason}")]
106    StoreCorrupted { path: String, reason: String },
107    #[error("not found: {0}")]
108    NotFound(String),
109    #[error("transaction not found: {0}")]
110    TxNotFound(TxId),
111    #[error("unique constraint violation: {table}.{column}")]
112    UniqueViolation { table: String, column: String },
113    #[error("foreign key violation: {table}.{column} references {ref_table}")]
114    ForeignKeyViolation {
115        table: String,
116        column: String,
117        ref_table: String,
118    },
119    #[error("recursive CTEs are not supported")]
120    RecursiveCteNotSupported,
121    #[error("window functions are not supported")]
122    WindowFunctionNotSupported,
123    #[error("stored procedures/functions are not supported")]
124    StoredProcNotSupported,
125    #[error("graph traversal requires explicit depth bound")]
126    UnboundedTraversal,
127    #[error("vector search requires LIMIT clause")]
128    UnboundedVectorSearch,
129    #[error("subqueries not supported; use CTE chaining")]
130    SubqueryNotSupported,
131    #[error("full-text search (WHERE column MATCH) is not supported")]
132    FullTextSearchNotSupported,
133    #[error("parse error: {0}")]
134    ParseError(String),
135    #[error("plan error: {0}")]
136    PlanError(String),
137    #[error("sync error: {0}")]
138    SyncError(String),
139    #[error("table {0} is not sync-eligible (no natural key)")]
140    NotSyncEligible(String),
141    #[error(
142        "cycle detected: inserting {edge_type} edge from {source_node} to {target_node} would create a cycle"
143    )]
144    CycleDetected {
145        edge_type: String,
146        source_node: uuid::Uuid,
147        target_node: uuid::Uuid,
148    },
149    #[error("plugin rejected at {hook}: {reason}")]
150    PluginRejected { hook: String, reason: String },
151    #[error(
152        "memory budget exceeded: {subsystem}/{operation} requested {requested_bytes} bytes, {available_bytes} available of {budget_limit_bytes} budget. Hint: {hint}"
153    )]
154    MemoryBudgetExceeded {
155        subsystem: String,
156        operation: String,
157        requested_bytes: usize,
158        available_bytes: usize,
159        budget_limit_bytes: usize,
160        hint: String,
161    },
162    #[error(
163        "disk budget exceeded: {operation} - file is {current_bytes} bytes, limit is {budget_limit_bytes} bytes. Hint: {hint}"
164    )]
165    DiskBudgetExceeded {
166        operation: String,
167        current_bytes: u64,
168        budget_limit_bytes: u64,
169        hint: String,
170    },
171    #[error("column type mismatch: {table}.{column} expected {expected}, got {actual}")]
172    ColumnTypeMismatch {
173        table: String,
174        column: String,
175        expected: &'static str,
176        actual: &'static str,
177    },
178    #[error("tx id out of range: {table}.{column} value {value} exceeds max {max}")]
179    TxIdOutOfRange {
180        table: String,
181        column: String,
182        value: u64,
183        max: u64,
184    },
185    #[error("tx id overflow: {table} incoming {incoming}")]
186    TxIdOverflow { table: String, incoming: u64 },
187    #[error("column {table}.{column} is NOT NULL")]
188    ColumnNotNullable { table: String, column: String },
189    #[error("index not found: {table}.{index}")]
190    IndexNotFound { table: String, index: String },
191    #[error("duplicate index: {table}.{index}")]
192    DuplicateIndex { table: String, index: String },
193    #[error("reserved index name: {table}.{name} uses reserved prefix `{prefix}`")]
194    ReservedIndexName {
195        table: String,
196        name: String,
197        prefix: String,
198    },
199    #[error("column not indexable: {table}.{column} has type {column_type:?}")]
200    ColumnNotIndexable {
201        table: String,
202        column: String,
203        column_type: crate::table_meta::ColumnType,
204    },
205    #[error("column in index: {table}.{column} referenced by index {index}")]
206    ColumnInIndex {
207        table: String,
208        column: String,
209        index: String,
210    },
211    #[error("column not found: {table}.{column}")]
212    ColumnNotFound { table: String, column: String },
213    #[error("{0}")]
214    Other(String),
215}
216
217fn drop_blocked_rank_policy_display(
218    table: &str,
219    column: &Option<Box<str>>,
220    dropped_index: &Option<Box<str>>,
221    policy_table: &str,
222    policy_column: &str,
223    sort_key: &str,
224) -> String {
225    if let Some(column) = column {
226        return format!(
227            "cannot drop or rename column `{table}.{column}`: rank policy `{sort_key}` on `{policy_table}.{policy_column}` depends on it"
228        );
229    }
230    if let Some(index) = dropped_index {
231        return format!(
232            "cannot drop index `{index}` on `{table}`: rank policy `{sort_key}` on `{policy_table}.{policy_column}` depends on it"
233        );
234    }
235    format!(
236        "cannot drop table `{table}`: rank policy `{sort_key}` on `{policy_table}.{policy_column}` depends on it"
237    )
238}
239
240pub type Result<T> = std::result::Result<T, Error>;