Skip to main content

graphrag_core/core/
error.rs

1//! Unified error handling for the GraphRAG system
2//!
3//! This module provides a centralized error type that encompasses all possible
4//! errors that can occur throughout the GraphRAG pipeline.
5
6use std::fmt;
7
8/// Main error type for the GraphRAG system
9#[derive(Debug)]
10pub enum GraphRAGError {
11    /// Configuration-related errors
12    Config {
13        /// Error message
14        message: String
15    },
16
17    /// System not initialized error with helpful guidance
18    NotInitialized,
19
20    /// No documents added error with helpful guidance
21    NoDocuments,
22
23    /// I/O errors from file operations
24    Io(std::io::Error),
25
26    /// HTTP request errors
27    #[cfg(feature = "ureq")]
28    Http(Box<ureq::Error>),
29
30    /// HTTP request errors (WASM-compatible)
31    #[cfg(not(feature = "ureq"))]
32    Http(String),
33
34    /// JSON parsing/serialization errors
35    Json(json::Error),
36
37    /// Serde JSON errors
38    SerdeJson(serde_json::Error),
39
40    /// Text processing errors
41    TextProcessing {
42        /// Error message
43        message: String
44    },
45
46    /// Graph construction and manipulation errors
47    GraphConstruction {
48        /// Error message
49        message: String
50    },
51
52    /// Vector search and embedding errors
53    VectorSearch {
54        /// Error message
55        message: String
56    },
57
58    /// Entity extraction errors
59    EntityExtraction {
60        /// Error message
61        message: String
62    },
63
64    /// Retrieval system errors
65    Retrieval {
66        /// Error message
67        message: String
68    },
69
70    /// Answer generation errors
71    Generation {
72        /// Error message
73        message: String
74    },
75
76    /// Function calling errors
77    FunctionCall {
78        /// Error message
79        message: String
80    },
81
82    /// Storage backend errors
83    Storage {
84        /// Error message
85        message: String
86    },
87
88    /// Embedding model errors
89    Embedding {
90        /// Error message
91        message: String
92    },
93
94    /// Language model errors
95    LanguageModel {
96        /// Error message
97        message: String
98    },
99
100    /// Parallel processing errors
101    Parallel {
102        /// Error message
103        message: String
104    },
105
106    /// Serialization errors
107    Serialization {
108        /// Error message
109        message: String
110    },
111
112    /// Validation errors
113    Validation {
114        /// Error message
115        message: String
116    },
117
118    /// Network connectivity errors
119    Network {
120        /// Error message
121        message: String
122    },
123
124    /// Authentication/authorization errors
125    Auth {
126        /// Error message
127        message: String
128    },
129
130    /// Resource not found errors
131    NotFound {
132        /// Resource type
133        resource: String,
134        /// Resource identifier
135        id: String
136    },
137
138    /// Already exists errors
139    AlreadyExists {
140        /// Resource type
141        resource: String,
142        /// Resource identifier
143        id: String
144    },
145
146    /// Operation timeout errors
147    Timeout {
148        /// Operation name
149        operation: String,
150        /// Timeout duration
151        duration: std::time::Duration,
152    },
153
154    /// Capacity/resource limit errors
155    ResourceLimit {
156        /// Resource name
157        resource: String,
158        /// Limit value
159        limit: usize
160    },
161
162    /// Data corruption or integrity errors
163    DataCorruption {
164        /// Error message
165        message: String
166    },
167
168    /// Unsupported operation errors
169    Unsupported {
170        /// Operation name
171        operation: String,
172        /// Reason for not supporting
173        reason: String
174    },
175
176    /// Rate limiting errors
177    RateLimit {
178        /// Error message
179        message: String
180    },
181
182    /// Conflict resolution errors
183    ConflictResolution {
184        /// Error message
185        message: String
186    },
187
188    /// Incremental update errors
189    IncrementalUpdate {
190        /// Error message
191        message: String
192    },
193}
194
195impl fmt::Display for GraphRAGError {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        match self {
198            GraphRAGError::Config { message } => {
199                write!(f, "Configuration error: {message}. \
200                          Solution: Check your config file or use default settings with GraphRAG::builder()")
201            }
202            GraphRAGError::NotInitialized => {
203                write!(
204                    f,
205                    "GraphRAG not initialized. \
206                          Solution: Call .initialize() or use .ask() which auto-initializes"
207                )
208            }
209            GraphRAGError::NoDocuments => {
210                write!(f, "No documents added. \
211                          Solution: Use .add_document(), .add_document_from_text(), or .from_file() to add content")
212            }
213            GraphRAGError::Io(err) => {
214                write!(
215                    f,
216                    "I/O error: {err}. \
217                          Solution: Check file permissions and that paths exist"
218                )
219            }
220            #[cfg(feature = "ureq")]
221            GraphRAGError::Http(err) => {
222                write!(
223                    f,
224                    "HTTP request error: {err}. \
225                          Solution: Check network connectivity and service availability"
226                )
227            }
228            #[cfg(not(feature = "ureq"))]
229            GraphRAGError::Http(msg) => {
230                write!(
231                    f,
232                    "HTTP request error: {msg}. \
233                          Solution: Check network connectivity and service availability"
234                )
235            }
236            GraphRAGError::Json(err) => {
237                write!(
238                    f,
239                    "JSON parsing error: {err}. \
240                          Solution: Verify JSON format or use default configuration"
241                )
242            }
243            GraphRAGError::SerdeJson(err) => {
244                write!(
245                    f,
246                    "JSON serialization error: {err}. \
247                          Solution: Verify data structure compatibility"
248                )
249            }
250            GraphRAGError::TextProcessing { message } => {
251                write!(
252                    f,
253                    "Text processing error: {message}. \
254                          Solution: Check text content and chunk size configuration"
255                )
256            }
257            GraphRAGError::GraphConstruction { message } => {
258                write!(
259                    f,
260                    "Graph construction error: {message}. \
261                          Solution: Initialize GraphRAG system and add documents first"
262                )
263            }
264            GraphRAGError::VectorSearch { message } => {
265                write!(f, "Vector search error: {message}. \
266                          Solution: Ensure embeddings are initialized with .initialize_embeddings()")
267            }
268            GraphRAGError::EntityExtraction { message } => {
269                write!(f, "Entity extraction error: {message}. \
270                          Solution: Check entity extraction configuration or use lower confidence threshold")
271            }
272            GraphRAGError::Retrieval { message } => {
273                write!(
274                    f,
275                    "Retrieval error: {message}. \
276                          Solution: Ensure documents are added and graph is built"
277                )
278            }
279            GraphRAGError::Generation { message } => {
280                write!(f, "Answer generation error: {message}. \
281                          Solution: Check LLM provider configuration or use GraphRAG::builder().auto_detect_llm()")
282            }
283            GraphRAGError::FunctionCall { message } => {
284                write!(f, "Function call error: {message}")
285            }
286            GraphRAGError::Storage { message } => {
287                write!(f, "Storage error: {message}")
288            }
289            GraphRAGError::Embedding { message } => {
290                write!(f, "Embedding error: {message}")
291            }
292            GraphRAGError::LanguageModel { message } => {
293                write!(f, "Language model error: {message}")
294            }
295            GraphRAGError::Parallel { message } => {
296                write!(f, "Parallel processing error: {message}")
297            }
298            GraphRAGError::Serialization { message } => {
299                write!(f, "Serialization error: {message}")
300            }
301            GraphRAGError::Validation { message } => {
302                write!(f, "Validation error: {message}")
303            }
304            GraphRAGError::Network { message } => {
305                write!(f, "Network error: {message}")
306            }
307            GraphRAGError::Auth { message } => {
308                write!(f, "Authentication error: {message}")
309            }
310            GraphRAGError::NotFound { resource, id } => {
311                write!(f, "{resource} not found: {id}")
312            }
313            GraphRAGError::AlreadyExists { resource, id } => {
314                write!(f, "{resource} already exists: {id}")
315            }
316            GraphRAGError::Timeout {
317                operation,
318                duration,
319            } => {
320                write!(f, "Operation '{operation}' timed out after {duration:?}")
321            }
322            GraphRAGError::ResourceLimit { resource, limit } => {
323                write!(f, "Resource limit exceeded for {resource}: {limit}")
324            }
325            GraphRAGError::DataCorruption { message } => {
326                write!(f, "Data corruption detected: {message}")
327            }
328            GraphRAGError::Unsupported { operation, reason } => {
329                write!(f, "Unsupported operation '{operation}': {reason}")
330            }
331            GraphRAGError::RateLimit { message } => {
332                write!(f, "Rate limit error: {message}")
333            }
334            GraphRAGError::ConflictResolution { message } => {
335                write!(f, "Conflict resolution error: {message}")
336            }
337            GraphRAGError::IncrementalUpdate { message } => {
338                write!(f, "Incremental update error: {message}")
339            }
340        }
341    }
342}
343
344impl std::error::Error for GraphRAGError {
345    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
346        match self {
347            GraphRAGError::Io(err) => Some(err),
348            #[cfg(feature = "ureq")]
349            GraphRAGError::Http(err) => Some(err.as_ref()),
350            #[cfg(not(feature = "ureq"))]
351            GraphRAGError::Http(_) => None,
352            GraphRAGError::Json(err) => Some(err),
353            GraphRAGError::SerdeJson(err) => Some(err),
354            _ => None,
355        }
356    }
357}
358
359// Automatic conversions from common error types
360impl From<std::io::Error> for GraphRAGError {
361    fn from(err: std::io::Error) -> Self {
362        GraphRAGError::Io(err)
363    }
364}
365
366#[cfg(feature = "ureq")]
367impl From<ureq::Error> for GraphRAGError {
368    fn from(err: ureq::Error) -> Self {
369        GraphRAGError::Http(Box::new(err))
370    }
371}
372
373impl From<json::Error> for GraphRAGError {
374    fn from(err: json::Error) -> Self {
375        GraphRAGError::Json(err)
376    }
377}
378
379impl From<serde_json::Error> for GraphRAGError {
380    fn from(err: serde_json::Error) -> Self {
381        GraphRAGError::SerdeJson(err)
382    }
383}
384
385// ROGRAG error conversions
386#[cfg(feature = "rograg")]
387impl From<crate::rograg::logic_form::LogicFormError> for GraphRAGError {
388    fn from(err: crate::rograg::logic_form::LogicFormError) -> Self {
389        GraphRAGError::Retrieval {
390            message: format!("Logic form error: {err}"),
391        }
392    }
393}
394
395#[cfg(feature = "rograg")]
396impl From<crate::rograg::processor::ProcessingError> for GraphRAGError {
397    fn from(err: crate::rograg::processor::ProcessingError) -> Self {
398        GraphRAGError::Generation {
399            message: format!("Processing error: {err}"),
400        }
401    }
402}
403
404#[cfg(feature = "rograg")]
405impl From<crate::rograg::quality_metrics::MetricsError> for GraphRAGError {
406    fn from(err: crate::rograg::quality_metrics::MetricsError) -> Self {
407        GraphRAGError::Validation {
408            message: format!("Metrics error: {err}"),
409        }
410    }
411}
412
413#[cfg(feature = "rograg")]
414impl From<crate::rograg::streaming::StreamingError> for GraphRAGError {
415    fn from(err: crate::rograg::streaming::StreamingError) -> Self {
416        GraphRAGError::Generation {
417            message: format!("Streaming error: {err}"),
418        }
419    }
420}
421
422#[cfg(feature = "rograg")]
423impl From<crate::rograg::fuzzy_matcher::FuzzyMatchError> for GraphRAGError {
424    fn from(err: crate::rograg::fuzzy_matcher::FuzzyMatchError) -> Self {
425        GraphRAGError::Retrieval {
426            message: format!("Fuzzy match error: {err}"),
427        }
428    }
429}
430
431/// Convenient Result type alias
432pub type Result<T> = std::result::Result<T, GraphRAGError>;
433
434/// Trait for adding context to errors
435pub trait ErrorContext<T> {
436    /// Add context to an error
437    fn with_context(self, context: &str) -> Result<T>;
438
439    /// Add context using a closure
440    fn with_context_lazy<F>(self, f: F) -> Result<T>
441    where
442        F: FnOnce() -> String;
443}
444
445impl<T, E> ErrorContext<T> for std::result::Result<T, E>
446where
447    E: Into<GraphRAGError>,
448{
449    fn with_context(self, context: &str) -> Result<T> {
450        self.map_err(|e| {
451            let base_error = e.into();
452            match base_error {
453                GraphRAGError::Config { message } => GraphRAGError::Config {
454                    message: format!("{context}: {message}"),
455                },
456                GraphRAGError::TextProcessing { message } => GraphRAGError::TextProcessing {
457                    message: format!("{context}: {message}"),
458                },
459                GraphRAGError::GraphConstruction { message } => GraphRAGError::GraphConstruction {
460                    message: format!("{context}: {message}"),
461                },
462                GraphRAGError::VectorSearch { message } => GraphRAGError::VectorSearch {
463                    message: format!("{context}: {message}"),
464                },
465                GraphRAGError::EntityExtraction { message } => GraphRAGError::EntityExtraction {
466                    message: format!("{context}: {message}"),
467                },
468                GraphRAGError::Retrieval { message } => GraphRAGError::Retrieval {
469                    message: format!("{context}: {message}"),
470                },
471                GraphRAGError::Generation { message } => GraphRAGError::Generation {
472                    message: format!("{context}: {message}"),
473                },
474                GraphRAGError::FunctionCall { message } => GraphRAGError::FunctionCall {
475                    message: format!("{context}: {message}"),
476                },
477                GraphRAGError::Storage { message } => GraphRAGError::Storage {
478                    message: format!("{context}: {message}"),
479                },
480                GraphRAGError::Embedding { message } => GraphRAGError::Embedding {
481                    message: format!("{context}: {message}"),
482                },
483                GraphRAGError::LanguageModel { message } => GraphRAGError::LanguageModel {
484                    message: format!("{context}: {message}"),
485                },
486                GraphRAGError::Parallel { message } => GraphRAGError::Parallel {
487                    message: format!("{context}: {message}"),
488                },
489                GraphRAGError::Serialization { message } => GraphRAGError::Serialization {
490                    message: format!("{context}: {message}"),
491                },
492                GraphRAGError::Validation { message } => GraphRAGError::Validation {
493                    message: format!("{context}: {message}"),
494                },
495                GraphRAGError::Network { message } => GraphRAGError::Network {
496                    message: format!("{context}: {message}"),
497                },
498                GraphRAGError::Auth { message } => GraphRAGError::Auth {
499                    message: format!("{context}: {message}"),
500                },
501                GraphRAGError::DataCorruption { message } => GraphRAGError::DataCorruption {
502                    message: format!("{context}: {message}"),
503                },
504                GraphRAGError::RateLimit { message } => GraphRAGError::RateLimit {
505                    message: format!("{context}: {message}"),
506                },
507                GraphRAGError::ConflictResolution { message } => {
508                    GraphRAGError::ConflictResolution {
509                        message: format!("{context}: {message}"),
510                    }
511                }
512                GraphRAGError::IncrementalUpdate { message } => GraphRAGError::IncrementalUpdate {
513                    message: format!("{context}: {message}"),
514                },
515                other => other, // For errors that don't have a message field
516            }
517        })
518    }
519
520    fn with_context_lazy<F>(self, f: F) -> Result<T>
521    where
522        F: FnOnce() -> String,
523    {
524        match self {
525            Ok(value) => Ok(value),
526            Err(e) => {
527                let context = f();
528                Err(e).with_context(&context)
529            }
530        }
531    }
532}
533
534/// Helper macros for creating specific error types
535///
536/// Creates a configuration error with a message
537#[macro_export]
538macro_rules! config_error {
539    ($msg:expr) => {
540        $crate::GraphRAGError::Config {
541            message: $msg.to_string(),
542        }
543    };
544    ($fmt:expr, $($arg:tt)*) => {
545        $crate::GraphRAGError::Config {
546            message: format!($fmt, $($arg)*),
547        }
548    };
549}
550
551/// Creates a storage error with a message
552#[macro_export]
553macro_rules! storage_error {
554    ($msg:expr) => {
555        $crate::GraphRAGError::Storage {
556            message: $msg.to_string(),
557        }
558    };
559    ($fmt:expr, $($arg:tt)*) => {
560        $crate::GraphRAGError::Storage {
561            message: format!($fmt, $($arg)*),
562        }
563    };
564}
565
566/// Creates a retrieval error with a message
567#[macro_export]
568macro_rules! retrieval_error {
569    ($msg:expr) => {
570        $crate::GraphRAGError::Retrieval {
571            message: $msg.to_string(),
572        }
573    };
574    ($fmt:expr, $($arg:tt)*) => {
575        $crate::GraphRAGError::Retrieval {
576            message: format!($fmt, $($arg)*),
577        }
578    };
579}
580
581/// Creates a generation error with a message
582#[macro_export]
583macro_rules! generation_error {
584    ($msg:expr) => {
585        $crate::GraphRAGError::Generation {
586            message: $msg.to_string(),
587        }
588    };
589    ($fmt:expr, $($arg:tt)*) => {
590        $crate::GraphRAGError::Generation {
591            message: format!($fmt, $($arg)*),
592        }
593    };
594}
595
596/// Error severity levels for logging and monitoring
597#[derive(Debug, Clone, Copy, PartialEq, Eq)]
598pub enum ErrorSeverity {
599    /// Informational - not actually an error
600    Info,
601    /// Warning - something unexpected but recoverable
602    Warning,
603    /// Error - operation failed but system can continue
604    Error,
605    /// Critical - system integrity compromised
606    Critical,
607}
608
609impl GraphRAGError {
610    /// Get the severity level of this error
611    pub fn severity(&self) -> ErrorSeverity {
612        match self {
613            GraphRAGError::Config { .. } => ErrorSeverity::Critical,
614            GraphRAGError::NotInitialized => ErrorSeverity::Warning,
615            GraphRAGError::NoDocuments => ErrorSeverity::Warning,
616            GraphRAGError::Io(_) => ErrorSeverity::Error,
617            GraphRAGError::Http(_) => ErrorSeverity::Warning,
618            GraphRAGError::Json(_) | GraphRAGError::SerdeJson(_) => ErrorSeverity::Error,
619            GraphRAGError::TextProcessing { .. } => ErrorSeverity::Warning,
620            GraphRAGError::GraphConstruction { .. } => ErrorSeverity::Error,
621            GraphRAGError::VectorSearch { .. } => ErrorSeverity::Warning,
622            GraphRAGError::EntityExtraction { .. } => ErrorSeverity::Warning,
623            GraphRAGError::Retrieval { .. } => ErrorSeverity::Warning,
624            GraphRAGError::Generation { .. } => ErrorSeverity::Warning,
625            GraphRAGError::FunctionCall { .. } => ErrorSeverity::Warning,
626            GraphRAGError::Storage { .. } => ErrorSeverity::Error,
627            GraphRAGError::Embedding { .. } => ErrorSeverity::Warning,
628            GraphRAGError::LanguageModel { .. } => ErrorSeverity::Warning,
629            GraphRAGError::Parallel { .. } => ErrorSeverity::Error,
630            GraphRAGError::Serialization { .. } => ErrorSeverity::Error,
631            GraphRAGError::Validation { .. } => ErrorSeverity::Error,
632            GraphRAGError::Network { .. } => ErrorSeverity::Warning,
633            GraphRAGError::Auth { .. } => ErrorSeverity::Error,
634            GraphRAGError::NotFound { .. } => ErrorSeverity::Warning,
635            GraphRAGError::AlreadyExists { .. } => ErrorSeverity::Warning,
636            GraphRAGError::Timeout { .. } => ErrorSeverity::Warning,
637            GraphRAGError::ResourceLimit { .. } => ErrorSeverity::Error,
638            GraphRAGError::DataCorruption { .. } => ErrorSeverity::Critical,
639            GraphRAGError::Unsupported { .. } => ErrorSeverity::Error,
640            GraphRAGError::RateLimit { .. } => ErrorSeverity::Warning,
641            GraphRAGError::ConflictResolution { .. } => ErrorSeverity::Error,
642            GraphRAGError::IncrementalUpdate { .. } => ErrorSeverity::Error,
643        }
644    }
645
646    /// Check if this error is recoverable
647    pub fn is_recoverable(&self) -> bool {
648        match self.severity() {
649            ErrorSeverity::Info | ErrorSeverity::Warning => true,
650            ErrorSeverity::Error => false,
651            ErrorSeverity::Critical => false,
652        }
653    }
654
655    /// Get error category for metrics/monitoring
656    pub fn category(&self) -> &'static str {
657        match self {
658            GraphRAGError::Config { .. } => "config",
659            GraphRAGError::NotInitialized => "initialization",
660            GraphRAGError::NoDocuments => "usage",
661            GraphRAGError::Io(_) => "io",
662            GraphRAGError::Http(_) => "http",
663            GraphRAGError::Json(_) | GraphRAGError::SerdeJson(_) => "serialization",
664            GraphRAGError::TextProcessing { .. } => "text_processing",
665            GraphRAGError::GraphConstruction { .. } => "graph",
666            GraphRAGError::VectorSearch { .. } => "vector_search",
667            GraphRAGError::EntityExtraction { .. } => "entity_extraction",
668            GraphRAGError::Retrieval { .. } => "retrieval",
669            GraphRAGError::Generation { .. } => "generation",
670            GraphRAGError::FunctionCall { .. } => "function_calling",
671            GraphRAGError::Storage { .. } => "storage",
672            GraphRAGError::Embedding { .. } => "embedding",
673            GraphRAGError::LanguageModel { .. } => "language_model",
674            GraphRAGError::Parallel { .. } => "parallel",
675            GraphRAGError::Serialization { .. } => "serialization",
676            GraphRAGError::Validation { .. } => "validation",
677            GraphRAGError::Network { .. } => "network",
678            GraphRAGError::Auth { .. } => "auth",
679            GraphRAGError::NotFound { .. } => "not_found",
680            GraphRAGError::AlreadyExists { .. } => "already_exists",
681            GraphRAGError::Timeout { .. } => "timeout",
682            GraphRAGError::ResourceLimit { .. } => "resource_limit",
683            GraphRAGError::DataCorruption { .. } => "data_corruption",
684            GraphRAGError::Unsupported { .. } => "unsupported",
685            GraphRAGError::RateLimit { .. } => "rate_limit",
686            GraphRAGError::ConflictResolution { .. } => "conflict_resolution",
687            GraphRAGError::IncrementalUpdate { .. } => "incremental_update",
688        }
689    }
690}
691
692impl From<regex::Error> for GraphRAGError {
693    fn from(err: regex::Error) -> Self {
694        GraphRAGError::Validation {
695            message: format!("Regex error: {err}"),
696        }
697    }
698}
699
700#[cfg(test)]
701mod tests {
702    use super::*;
703
704    #[test]
705    fn test_error_display() {
706        let error = GraphRAGError::Config {
707            message: "Invalid configuration".to_string(),
708        };
709        assert_eq!(
710            format!("{error}"),
711            "Configuration error: Invalid configuration. Solution: Check your config file or use default settings with GraphRAG::builder()"
712        );
713    }
714
715    #[test]
716    fn test_error_context() {
717        let result: std::result::Result<(), std::io::Error> = Err(std::io::Error::new(
718            std::io::ErrorKind::NotFound,
719            "file not found",
720        ));
721
722        let error = result.with_context("loading configuration").unwrap_err();
723        assert!(matches!(error, GraphRAGError::Io(_)));
724    }
725
726    #[test]
727    fn test_error_macros() {
728        let error = config_error!("test message");
729        assert!(matches!(error, GraphRAGError::Config { .. }));
730
731        let error = storage_error!("test {} {}", "formatted", "message");
732        assert!(matches!(error, GraphRAGError::Storage { .. }));
733    }
734
735    #[test]
736    fn test_error_severity() {
737        let config_error = GraphRAGError::Config {
738            message: "test".to_string(),
739        };
740        assert_eq!(config_error.severity(), ErrorSeverity::Critical);
741        assert!(!config_error.is_recoverable());
742
743        let warning_error = GraphRAGError::Retrieval {
744            message: "test".to_string(),
745        };
746        assert_eq!(warning_error.severity(), ErrorSeverity::Warning);
747        assert!(warning_error.is_recoverable());
748    }
749}