Skip to main content

engram_core/
error.rs

1use std::fmt;
2
3use engram_consolidate::ConsolidateError;
4use engram_hnsw::HnswError;
5use engram_llm_client::ApiError;
6use engram_storage::StorageError;
7
8#[derive(Debug)]
9pub enum CoreError {
10    ConfigNotFound,
11    ConfigParseError(String),
12    InvalidProvider(String),
13    IndexCorrupted(String),
14    RebuildFailed(String),
15    SocketError(String),
16    DispatchError(String),
17    ConfigReadOnly,
18    ExportFailed(String),
19    ImportVersionMismatch(u64),
20    ImportFailed(String),
21    InitFailed(String),
22    Storage(StorageError),
23    Hnsw(HnswError),
24    Api(ApiError),
25    TrainerFailed(String),
26    TrainerTimeout,
27    TrainerMalformedOutput(String),
28    ProjectDirNotFound,
29    LegacyDatabaseDetected {
30        legacy_path: String,
31        project_path: String,
32    },
33    MigrationSourceNotFound,
34    MigrationFailed(String),
35    EmbeddingModelMismatch {
36        stored: String,
37        configured: String,
38    },
39    IndexHashCollision {
40        hash: u64,
41        existing_id: String,
42        conflicting_id: String,
43    },
44    ConfigValidation(String),
45    Consolidation(ConsolidateError),
46}
47
48impl fmt::Display for CoreError {
49    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50        match self {
51            Self::ConfigNotFound => {
52                write!(formatter, "[6001] config not found")
53            }
54            Self::ConfigParseError(message) => {
55                write!(formatter, "[6002] config parse error: {message}")
56            }
57            Self::InvalidProvider(message) => {
58                write!(formatter, "[6003] invalid provider: {message}")
59            }
60            Self::IndexCorrupted(message) => {
61                write!(formatter, "[6004] index corrupted: {message}")
62            }
63            Self::RebuildFailed(message) => {
64                write!(formatter, "[6005] rebuild failed: {message}")
65            }
66            Self::SocketError(message) => {
67                write!(formatter, "[6006] socket error: {message}")
68            }
69            Self::DispatchError(message) => {
70                write!(formatter, "[6007] dispatch error: {message}")
71            }
72            Self::ConfigReadOnly => {
73                write!(formatter, "[6008] config is read-only")
74            }
75            Self::ExportFailed(message) => {
76                write!(formatter, "[6009] export failed: {message}")
77            }
78            Self::ImportVersionMismatch(version) => {
79                write!(
80                    formatter,
81                    "[6010] import version mismatch: expected 1, got {version}"
82                )
83            }
84            Self::ImportFailed(message) => {
85                write!(formatter, "[6011] import failed: {message}")
86            }
87            Self::InitFailed(message) => {
88                write!(formatter, "[6012] init failed: {message}")
89            }
90            Self::TrainerFailed(message) => {
91                write!(formatter, "[6013] trainer failed: {message}")
92            }
93            Self::TrainerTimeout => {
94                write!(formatter, "[6014] trainer timeout")
95            }
96            Self::TrainerMalformedOutput(message) => {
97                write!(formatter, "[6015] trainer malformed output: {message}")
98            }
99            Self::ProjectDirNotFound => {
100                write!(
101                    formatter,
102                    "[6016] project directory not found: no .engram/ in cwd or ancestors (run 'engram init')"
103                )
104            }
105            Self::LegacyDatabaseDetected {
106                legacy_path,
107                project_path,
108            } => {
109                write!(
110                    formatter,
111                    "[6017] legacy global database detected at {legacy_path}. Run `engram migrate` to import into project {project_path}, or `engram init` to start fresh."
112                )
113            }
114            Self::MigrationSourceNotFound => {
115                write!(
116                    formatter,
117                    "[6018] migration source not found: no legacy database at ~/.engram/engram.db (nothing to migrate)"
118                )
119            }
120            Self::MigrationFailed(message) => {
121                write!(formatter, "[6019] migration failed: {message}")
122            }
123            Self::EmbeddingModelMismatch { stored, configured } => {
124                write!(
125                    formatter,
126                    "[6020] embedding model mismatch: database was last embedded with `{stored}`, but config specifies `{configured}`. Run `engram reembed` to re-compute embeddings, then restart the daemon."
127                )
128            }
129            Self::IndexHashCollision {
130                hash,
131                existing_id,
132                conflicting_id,
133            } => {
134                write!(
135                    formatter,
136                    "[6021] index hash collision: hash {hash:#x} already mapped to '{existing_id}', refusing '{conflicting_id}'"
137                )
138            }
139            Self::ConfigValidation(message) => {
140                write!(formatter, "[6022] invalid configuration: {message}")
141            }
142            Self::Storage(error) => error.fmt(formatter),
143            Self::Hnsw(error) => error.fmt(formatter),
144            Self::Api(error) => error.fmt(formatter),
145            Self::Consolidation(error) => error.fmt(formatter),
146        }
147    }
148}
149
150impl std::error::Error for CoreError {
151    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
152        match self {
153            Self::Storage(error) => Some(error),
154            Self::Hnsw(error) => Some(error),
155            Self::Api(error) => Some(error),
156            Self::Consolidation(error) => Some(error),
157            _ => None,
158        }
159    }
160}
161
162impl From<StorageError> for CoreError {
163    fn from(error: StorageError) -> Self {
164        Self::Storage(error)
165    }
166}
167
168impl From<HnswError> for CoreError {
169    fn from(error: HnswError) -> Self {
170        Self::Hnsw(error)
171    }
172}
173
174impl From<ApiError> for CoreError {
175    fn from(error: ApiError) -> Self {
176        Self::Api(error)
177    }
178}
179
180impl From<ConsolidateError> for CoreError {
181    fn from(error: ConsolidateError) -> Self {
182        Self::Consolidation(error)
183    }
184}