systemprompt_extension/
error.rs1use thiserror::Error;
7
8#[derive(Debug, Error)]
9pub enum LoaderError {
10 #[error("Extension '{extension}' requires dependency '{dependency}' which is not registered")]
11 MissingDependency {
12 extension: String,
13 dependency: String,
14 },
15
16 #[error("Extension with ID '{0}' is already registered")]
17 DuplicateExtension(String),
18
19 #[error("Failed to initialize extension '{extension}': {message}")]
20 InitializationFailed { extension: String, message: String },
21
22 #[error("Failed to install schema for extension '{extension}': {message}")]
23 SchemaInstallationFailed { extension: String, message: String },
24
25 #[error("Migration failed for extension '{extension}': {message}")]
26 MigrationFailed { extension: String, message: String },
27
28 #[error(
29 "Migration {version} for extension '{extension}' is not reversible (no down SQL provided)"
30 )]
31 MigrationNotReversible { extension: String, version: u32 },
32
33 #[error(
34 "Extension '{extension}' migration slot {version} was applied as '{stored_name}' but the \
35 tree now names it '{current_name}'. A migration file was deleted and its number reused; \
36 established databases have already spent that slot, so the new SQL would be skipped, \
37 never executed. Renumber it above every used slot and leave a \
38 `{version:03}_{stored_name}.tombstone` behind so the number cannot be claimed again."
39 )]
40 MigrationSlotReused {
41 extension: String,
42 version: u32,
43 stored_name: String,
44 current_name: String,
45 },
46
47 #[error("Configuration validation failed for extension '{extension}': {message}")]
48 ConfigValidationFailed { extension: String, message: String },
49
50 #[error("Extension '{extension}' uses reserved API path '{path}'")]
51 ReservedPathCollision { extension: String, path: String },
52
53 #[error("Extension '{extension}' has invalid base path '{path}': must start with /api/")]
54 InvalidBasePath { extension: String, path: String },
55
56 #[error("Dependency cycle detected while ordering extensions: {chain}")]
57 DependencyCycle { chain: String },
58
59 #[error(
60 "Extension '{extension}' migration ALTERs table '{table}' but does not create it in its \
61 schemas() nor declare it in cross_extension_tables(); cross-extension table mutations \
62 must be declared explicitly"
63 )]
64 CrossExtensionAlterUndeclared { extension: String, table: String },
65
66 #[error(
67 "Table '{table}' is created by both extension '{extension_a}' and '{extension_b}'; every \
68 table must be declared by exactly one extension"
69 )]
70 DuplicateTableOwner {
71 table: String,
72 extension_a: String,
73 extension_b: String,
74 },
75
76 #[error(
77 "Extension '{extension}' declares cross_extension_tables() entry '{table}', which is not \
78 a table created by any other loaded extension"
79 )]
80 CrossExtensionTableNotOwned { extension: String, table: String },
81
82 #[error(
83 "Extension '{extension}' seed '{seed}' contains forbidden statement '{statement}'; seeds \
84 may only contain INSERT … ON CONFLICT, UPDATE, MERGE, or WITH … INSERT"
85 )]
86 InvalidSeedStatement {
87 extension: String,
88 seed: String,
89 statement: String,
90 },
91
92 #[error(
93 "Extension '{extension}' seed '{seed}' contains a bare INSERT with no ON CONFLICT clause; \
94 seeds run on every boot and must be idempotent — add ON CONFLICT … DO NOTHING/UPDATE"
95 )]
96 SeedInsertNotIdempotent { extension: String, seed: String },
97
98 #[error("Extension '{extension}' seed '{seed}' failed to parse or apply: {message}")]
99 SeedFailed {
100 extension: String,
101 seed: String,
102 message: String,
103 },
104}
105
106#[derive(Debug, Error)]
107pub enum ConfigError {
108 #[error("Configuration key '{0}' not found")]
109 NotFound(String),
110
111 #[error("Invalid configuration value for '{key}': {message}")]
112 InvalidValue { key: String, message: String },
113
114 #[error("Failed to parse configuration: {message}")]
115 ParseError { message: String },
116
117 #[error("Schema validation failed: {0}")]
118 SchemaValidation(String),
119}