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 Consolidation(ConsolidateError),
40}
41
42impl fmt::Display for CoreError {
43 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self {
45 Self::ConfigNotFound => {
46 write!(formatter, "[6001] config not found")
47 }
48 Self::ConfigParseError(message) => {
49 write!(formatter, "[6002] config parse error: {message}")
50 }
51 Self::InvalidProvider(message) => {
52 write!(formatter, "[6003] invalid provider: {message}")
53 }
54 Self::IndexCorrupted(message) => {
55 write!(formatter, "[6004] index corrupted: {message}")
56 }
57 Self::RebuildFailed(message) => {
58 write!(formatter, "[6005] rebuild failed: {message}")
59 }
60 Self::SocketError(message) => {
61 write!(formatter, "[6006] socket error: {message}")
62 }
63 Self::DispatchError(message) => {
64 write!(formatter, "[6007] dispatch error: {message}")
65 }
66 Self::ConfigReadOnly => {
67 write!(formatter, "[6008] config is read-only")
68 }
69 Self::ExportFailed(message) => {
70 write!(formatter, "[6009] export failed: {message}")
71 }
72 Self::ImportVersionMismatch(version) => {
73 write!(
74 formatter,
75 "[6010] import version mismatch: expected 1, got {version}"
76 )
77 }
78 Self::ImportFailed(message) => {
79 write!(formatter, "[6011] import failed: {message}")
80 }
81 Self::InitFailed(message) => {
82 write!(formatter, "[6012] init failed: {message}")
83 }
84 Self::TrainerFailed(message) => {
85 write!(formatter, "[6013] trainer failed: {message}")
86 }
87 Self::TrainerTimeout => {
88 write!(formatter, "[6014] trainer timeout")
89 }
90 Self::TrainerMalformedOutput(message) => {
91 write!(formatter, "[6015] trainer malformed output: {message}")
92 }
93 Self::ProjectDirNotFound => {
94 write!(
95 formatter,
96 "[6016] project directory not found: no .engram/ in cwd or ancestors (run 'engram init')"
97 )
98 }
99 Self::LegacyDatabaseDetected {
100 legacy_path,
101 project_path,
102 } => {
103 write!(
104 formatter,
105 "[6017] legacy global database detected at {legacy_path}. Run `engram migrate` to import into project {project_path}, or `engram init` to start fresh."
106 )
107 }
108 Self::MigrationSourceNotFound => {
109 write!(
110 formatter,
111 "[6018] migration source not found: no legacy database at ~/.engram/engram.db (nothing to migrate)"
112 )
113 }
114 Self::MigrationFailed(message) => {
115 write!(formatter, "[6019] migration failed: {message}")
116 }
117 Self::EmbeddingModelMismatch { stored, configured } => {
118 write!(
119 formatter,
120 "[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."
121 )
122 }
123 Self::Storage(error) => error.fmt(formatter),
124 Self::Hnsw(error) => error.fmt(formatter),
125 Self::Api(error) => error.fmt(formatter),
126 Self::Consolidation(error) => error.fmt(formatter),
127 }
128 }
129}
130
131impl std::error::Error for CoreError {
132 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
133 match self {
134 Self::Storage(error) => Some(error),
135 Self::Hnsw(error) => Some(error),
136 Self::Api(error) => Some(error),
137 Self::Consolidation(error) => Some(error),
138 _ => None,
139 }
140 }
141}
142
143impl From<StorageError> for CoreError {
144 fn from(error: StorageError) -> Self {
145 Self::Storage(error)
146 }
147}
148
149impl From<HnswError> for CoreError {
150 fn from(error: HnswError) -> Self {
151 Self::Hnsw(error)
152 }
153}
154
155impl From<ApiError> for CoreError {
156 fn from(error: ApiError) -> Self {
157 Self::Api(error)
158 }
159}
160
161impl From<ConsolidateError> for CoreError {
162 fn from(error: ConsolidateError) -> Self {
163 Self::Consolidation(error)
164 }
165}