Skip to main content

systemprompt_extension/
error.rs

1//! Typed error enums raised by extension registration and configuration.
2
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6pub enum LoaderError {
7    #[error("Extension '{extension}' requires dependency '{dependency}' which is not registered")]
8    MissingDependency {
9        extension: String,
10        dependency: String,
11    },
12
13    #[error("Extension with ID '{0}' is already registered")]
14    DuplicateExtension(String),
15
16    #[error("Failed to initialize extension '{extension}': {message}")]
17    InitializationFailed { extension: String, message: String },
18
19    #[error("Failed to install schema for extension '{extension}': {message}")]
20    SchemaInstallationFailed { extension: String, message: String },
21
22    #[error("Migration failed for extension '{extension}': {message}")]
23    MigrationFailed { extension: String, message: String },
24
25    #[error(
26        "Migration {version} for extension '{extension}' is not reversible (no down SQL provided)"
27    )]
28    MigrationNotReversible { extension: String, version: u32 },
29
30    #[error("Configuration validation failed for extension '{extension}': {message}")]
31    ConfigValidationFailed { extension: String, message: String },
32
33    #[error("Extension '{extension}' uses reserved API path '{path}'")]
34    ReservedPathCollision { extension: String, path: String },
35
36    #[error("Extension '{extension}' has invalid base path '{path}': must start with /api/")]
37    InvalidBasePath { extension: String, path: String },
38
39    #[error("Circular dependency detected: {chain}")]
40    CircularDependency { chain: String },
41
42    #[error("Dependency cycle detected while ordering extensions: {chain}")]
43    DependencyCycle { chain: String },
44
45    #[error(
46        "Extension '{extension}' migration ALTERs table '{table}' but does not create it in its \
47         schemas() nor declare it in cross_extension_tables(); cross-extension table mutations \
48         must be declared explicitly"
49    )]
50    CrossExtensionAlterUndeclared { extension: String, table: String },
51
52    #[error(
53        "Table '{table}' is created by both extension '{extension_a}' and '{extension_b}'; every \
54         table must be declared by exactly one extension"
55    )]
56    DuplicateTableOwner {
57        table: String,
58        extension_a: String,
59        extension_b: String,
60    },
61
62    #[error(
63        "Extension '{extension}' declares cross_extension_tables() entry '{table}', which is not \
64         a table created by any other loaded extension"
65    )]
66    CrossExtensionTableNotOwned { extension: String, table: String },
67
68    #[error(
69        "Extension '{extension}' seed '{seed}' contains forbidden statement '{statement}'; seeds \
70         may only contain INSERT … ON CONFLICT, UPDATE, MERGE, or WITH … INSERT"
71    )]
72    InvalidSeedStatement {
73        extension: String,
74        seed: String,
75        statement: String,
76    },
77
78    #[error(
79        "Extension '{extension}' seed '{seed}' contains a bare INSERT with no ON CONFLICT clause; \
80         seeds run on every boot and must be idempotent — add ON CONFLICT … DO NOTHING/UPDATE"
81    )]
82    SeedInsertNotIdempotent { extension: String, seed: String },
83
84    #[error("Extension '{extension}' seed '{seed}' failed to parse or apply: {message}")]
85    SeedFailed {
86        extension: String,
87        seed: String,
88        message: String,
89    },
90}
91
92#[derive(Debug, Error)]
93pub enum ConfigError {
94    #[error("Configuration key '{0}' not found")]
95    NotFound(String),
96
97    #[error("Invalid configuration value for '{key}': {message}")]
98    InvalidValue { key: String, message: String },
99
100    #[error("Failed to parse configuration: {0}")]
101    ParseError(String),
102
103    #[error("Schema validation failed: {0}")]
104    SchemaValidation(String),
105}