1use std::fmt;
7
8#[derive(Debug)]
10pub enum GraphRAGError {
11 Config {
13 message: String,
15 },
16
17 NotInitialized,
19
20 NoDocuments,
22
23 Io(std::io::Error),
25
26 #[cfg(feature = "ureq")]
28 Http(Box<ureq::Error>),
29
30 #[cfg(not(feature = "ureq"))]
32 Http(String),
33
34 Json(json::Error),
36
37 SerdeJson(serde_json::Error),
39
40 TextProcessing {
42 message: String,
44 },
45
46 GraphConstruction {
48 message: String,
50 },
51
52 VectorSearch {
54 message: String,
56 },
57
58 EntityExtraction {
60 message: String,
62 },
63
64 Retrieval {
66 message: String,
68 },
69
70 Generation {
72 message: String,
74 },
75
76 FunctionCall {
78 message: String,
80 },
81
82 Storage {
84 message: String,
86 },
87
88 Embedding {
90 message: String,
92 },
93
94 LanguageModel {
96 message: String,
98 },
99
100 Parallel {
102 message: String,
104 },
105
106 Serialization {
108 message: String,
110 },
111
112 Validation {
114 message: String,
116 },
117
118 Network {
120 message: String,
122 },
123
124 Auth {
126 message: String,
128 },
129
130 NotFound {
132 resource: String,
134 id: String,
136 },
137
138 AlreadyExists {
140 resource: String,
142 id: String,
144 },
145
146 Timeout {
148 operation: String,
150 duration: std::time::Duration,
152 },
153
154 ResourceLimit {
156 resource: String,
158 limit: usize,
160 },
161
162 DataCorruption {
164 message: String,
166 },
167
168 Unsupported {
170 operation: String,
172 reason: String,
174 },
175
176 RateLimit {
178 message: String,
180 },
181
182 ConflictResolution {
184 message: String,
186 },
187
188 IncrementalUpdate {
190 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
359impl 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#[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
431pub type Result<T> = std::result::Result<T, GraphRAGError>;
433
434pub trait ErrorContext<T> {
436 fn with_context(self, context: &str) -> Result<T>;
438
439 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, }
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#[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#[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#[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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
598pub enum ErrorSeverity {
599 Info,
601 Warning,
603 Error,
605 Critical,
607}
608
609#[derive(Debug, Clone)]
611pub struct ErrorSuggestion {
612 pub action: String,
614 pub code_example: Option<String>,
616 pub doc_link: Option<String>,
618}
619
620impl GraphRAGError {
621 pub fn severity(&self) -> ErrorSeverity {
623 match self {
624 GraphRAGError::Config { .. } => ErrorSeverity::Critical,
625 GraphRAGError::NotInitialized => ErrorSeverity::Warning,
626 GraphRAGError::NoDocuments => ErrorSeverity::Warning,
627 GraphRAGError::Io(_) => ErrorSeverity::Error,
628 GraphRAGError::Http(_) => ErrorSeverity::Warning,
629 GraphRAGError::Json(_) | GraphRAGError::SerdeJson(_) => ErrorSeverity::Error,
630 GraphRAGError::TextProcessing { .. } => ErrorSeverity::Warning,
631 GraphRAGError::GraphConstruction { .. } => ErrorSeverity::Error,
632 GraphRAGError::VectorSearch { .. } => ErrorSeverity::Warning,
633 GraphRAGError::EntityExtraction { .. } => ErrorSeverity::Warning,
634 GraphRAGError::Retrieval { .. } => ErrorSeverity::Warning,
635 GraphRAGError::Generation { .. } => ErrorSeverity::Warning,
636 GraphRAGError::FunctionCall { .. } => ErrorSeverity::Warning,
637 GraphRAGError::Storage { .. } => ErrorSeverity::Error,
638 GraphRAGError::Embedding { .. } => ErrorSeverity::Warning,
639 GraphRAGError::LanguageModel { .. } => ErrorSeverity::Warning,
640 GraphRAGError::Parallel { .. } => ErrorSeverity::Error,
641 GraphRAGError::Serialization { .. } => ErrorSeverity::Error,
642 GraphRAGError::Validation { .. } => ErrorSeverity::Error,
643 GraphRAGError::Network { .. } => ErrorSeverity::Warning,
644 GraphRAGError::Auth { .. } => ErrorSeverity::Error,
645 GraphRAGError::NotFound { .. } => ErrorSeverity::Warning,
646 GraphRAGError::AlreadyExists { .. } => ErrorSeverity::Warning,
647 GraphRAGError::Timeout { .. } => ErrorSeverity::Warning,
648 GraphRAGError::ResourceLimit { .. } => ErrorSeverity::Error,
649 GraphRAGError::DataCorruption { .. } => ErrorSeverity::Critical,
650 GraphRAGError::Unsupported { .. } => ErrorSeverity::Error,
651 GraphRAGError::RateLimit { .. } => ErrorSeverity::Warning,
652 GraphRAGError::ConflictResolution { .. } => ErrorSeverity::Error,
653 GraphRAGError::IncrementalUpdate { .. } => ErrorSeverity::Error,
654 }
655 }
656
657 pub fn is_recoverable(&self) -> bool {
659 match self.severity() {
660 ErrorSeverity::Info | ErrorSeverity::Warning => true,
661 ErrorSeverity::Error => false,
662 ErrorSeverity::Critical => false,
663 }
664 }
665
666 pub fn category(&self) -> &'static str {
668 match self {
669 GraphRAGError::Config { .. } => "config",
670 GraphRAGError::NotInitialized => "initialization",
671 GraphRAGError::NoDocuments => "usage",
672 GraphRAGError::Io(_) => "io",
673 GraphRAGError::Http(_) => "http",
674 GraphRAGError::Json(_) | GraphRAGError::SerdeJson(_) => "serialization",
675 GraphRAGError::TextProcessing { .. } => "text_processing",
676 GraphRAGError::GraphConstruction { .. } => "graph",
677 GraphRAGError::VectorSearch { .. } => "vector_search",
678 GraphRAGError::EntityExtraction { .. } => "entity_extraction",
679 GraphRAGError::Retrieval { .. } => "retrieval",
680 GraphRAGError::Generation { .. } => "generation",
681 GraphRAGError::FunctionCall { .. } => "function_calling",
682 GraphRAGError::Storage { .. } => "storage",
683 GraphRAGError::Embedding { .. } => "embedding",
684 GraphRAGError::LanguageModel { .. } => "language_model",
685 GraphRAGError::Parallel { .. } => "parallel",
686 GraphRAGError::Serialization { .. } => "serialization",
687 GraphRAGError::Validation { .. } => "validation",
688 GraphRAGError::Network { .. } => "network",
689 GraphRAGError::Auth { .. } => "auth",
690 GraphRAGError::NotFound { .. } => "not_found",
691 GraphRAGError::AlreadyExists { .. } => "already_exists",
692 GraphRAGError::Timeout { .. } => "timeout",
693 GraphRAGError::ResourceLimit { .. } => "resource_limit",
694 GraphRAGError::DataCorruption { .. } => "data_corruption",
695 GraphRAGError::Unsupported { .. } => "unsupported",
696 GraphRAGError::RateLimit { .. } => "rate_limit",
697 GraphRAGError::ConflictResolution { .. } => "conflict_resolution",
698 GraphRAGError::IncrementalUpdate { .. } => "incremental_update",
699 }
700 }
701
702 pub fn suggestion(&self) -> ErrorSuggestion {
718 match self {
719 GraphRAGError::Config { message } => {
720 let (action, code) = if message.contains("not found") || message.contains("load") {
721 (
722 "Create a config file or use defaults with Config::default()".to_string(),
723 Some(r#"// Option 1: Use defaults
724let config = Config::default();
725
726// Option 2: Load with hierarchy (user -> project -> env)
727let config = Config::load()?;
728
729// Option 3: Use TypedBuilder for compile-time safety
730let graphrag = TypedBuilder::new()
731 .with_output_dir("./output")
732 .with_ollama()
733 .build()?;"#.to_string())
734 )
735 } else {
736 (
737 "Check configuration values and TOML syntax".to_string(),
738 Some(r#"// Validate config before use
739let config = Config::from_toml_file("config.toml")?;
740
741// Or use hierarchical loading with env var overrides
742// Set GRAPHRAG_OLLAMA_HOST=localhost to override
743let config = Config::load()?;"#.to_string())
744 )
745 };
746 ErrorSuggestion { action, code_example: code, doc_link: None }
747 }
748 GraphRAGError::NotInitialized => ErrorSuggestion {
749 action: "Initialize the GraphRAG system before querying".to_string(),
750 code_example: Some(r#"// Option 1: Manual initialization
751let mut graphrag = GraphRAG::new(config)?;
752graphrag.initialize()?;
753
754// Option 2: Use quick_start (recommended)
755let mut graphrag = GraphRAG::quick_start("Your document text").await?;
756
757// Option 3: Use builder with auto-init
758let graphrag = TypedBuilder::new()
759 .with_output_dir("./output")
760 .with_ollama()
761 .build_and_init()?;"#.to_string()),
762 doc_link: None,
763 },
764 GraphRAGError::NoDocuments => ErrorSuggestion {
765 action: "Add documents before building the graph or querying".to_string(),
766 code_example: Some(r#"// Add text directly
767graphrag.add_document_from_text("Your document content here")?;
768
769// Add from file
770graphrag.add_document_from_file("document.txt")?;
771
772// Add multiple documents
773for file in glob("docs/*.txt")? {
774 graphrag.add_document_from_file(file)?;
775}
776
777// Then build the graph
778graphrag.build_graph().await?;"#.to_string()),
779 doc_link: None,
780 },
781 GraphRAGError::Http(_) => ErrorSuggestion {
782 action: "Check network connectivity and service availability".to_string(),
783 code_example: Some(r#"// Check Ollama is running
784// Terminal: curl http://localhost:11434/api/tags
785
786// Verify Ollama config
787let config = Config::default();
788assert!(config.ollama.enabled);
789assert_eq!(config.ollama.host, "localhost");
790assert_eq!(config.ollama.port, 11434);
791
792// Or use hash embeddings for offline mode
793let graphrag = TypedBuilder::new()
794 .with_output_dir("./output")
795 .with_hash_embeddings() // No network needed
796 .build()?;"#.to_string()),
797 doc_link: None,
798 },
799 GraphRAGError::LanguageModel { message } => {
800 let action = if message.contains("not found") || message.contains("model") {
801 "Ensure the LLM model is pulled and available".to_string()
802 } else {
803 "Check LLM provider configuration".to_string()
804 };
805 ErrorSuggestion {
806 action,
807 code_example: Some(r#"// Pull the model first (in terminal)
808// ollama pull llama3.2:latest
809// ollama pull nomic-embed-text:latest
810
811// Or specify a different model
812let graphrag = GraphRAGBuilder::new()
813 .with_ollama_enabled(true)
814 .with_chat_model("mistral:latest")
815 .with_ollama_embedding_model("nomic-embed-text:latest")
816 .build()?;"#.to_string()),
817 doc_link: None,
818 }
819 }
820 GraphRAGError::Embedding { message } => {
821 let action = if message.contains("dimension") {
822 "Check embedding dimension matches your model".to_string()
823 } else {
824 "Verify embedding provider configuration".to_string()
825 };
826 ErrorSuggestion {
827 action,
828 code_example: Some(r#"// Use matching dimension for your model
829// nomic-embed-text: 768, all-MiniLM-L6-v2: 384
830let graphrag = GraphRAGBuilder::new()
831 .with_embedding_dimension(768)
832 .with_embedding_backend("ollama")
833 .build()?;
834
835// Or use hash embeddings (dimension-agnostic)
836let graphrag = TypedBuilder::new()
837 .with_output_dir("./output")
838 .with_hash_embeddings()
839 .build()?;"#.to_string()),
840 doc_link: None,
841 }
842 }
843 GraphRAGError::Retrieval { .. } => ErrorSuggestion {
844 action: "Ensure documents are added and graph is built before querying".to_string(),
845 code_example: Some(r#"// Full workflow
846let mut graphrag = GraphRAG::new(config)?;
847graphrag.initialize()?;
848graphrag.add_document_from_text("Your content")?;
849graphrag.build_graph().await?;
850
851// Now you can query
852let answer = graphrag.ask("Your question").await?;"#.to_string()),
853 doc_link: None,
854 },
855 GraphRAGError::VectorSearch { .. } => ErrorSuggestion {
856 action: "Initialize embeddings and ensure vectors are indexed".to_string(),
857 code_example: Some(r#"// Make sure embeddings are configured
858let graphrag = GraphRAGBuilder::new()
859 .with_embedding_backend("ollama")
860 .with_embedding_model("nomic-embed-text:latest")
861 .build()?;
862
863// Or check if documents need reindexing
864graphrag.build_graph().await?;"#.to_string()),
865 doc_link: None,
866 },
867 GraphRAGError::Timeout { operation, duration } => ErrorSuggestion {
868 action: format!("Operation '{}' took too long ({:?}). Consider increasing timeout or optimizing.", operation, duration),
869 code_example: Some(r#"// Increase timeout in config
870let mut config = Config::default();
871config.ollama.timeout_seconds = 120; // 2 minutes
872
873// Or process smaller chunks
874config.chunk_size = 500; // Smaller chunks
875config.parallel.enabled = true; // Parallel processing"#.to_string()),
876 doc_link: None,
877 },
878 GraphRAGError::RateLimit { .. } => ErrorSuggestion {
879 action: "You've hit rate limits. Wait and retry, or use local models.".to_string(),
880 code_example: Some(r#"// Switch to local Ollama (no rate limits)
881let graphrag = TypedBuilder::new()
882 .with_output_dir("./output")
883 .with_ollama() // Local, no API limits
884 .build()?;
885
886// Or enable caching to reduce API calls
887let mut config = Config::default();
888config.caching.enabled = true;"#.to_string()),
889 doc_link: None,
890 },
891 GraphRAGError::Storage { .. } => ErrorSuggestion {
892 action: "Check file permissions and disk space".to_string(),
893 code_example: Some(r#"// Verify output directory exists and is writable
894std::fs::create_dir_all("./output")?;
895
896// Use a different output directory
897let graphrag = GraphRAGBuilder::new()
898 .with_output_dir("/tmp/graphrag_output")
899 .build()?;"#.to_string()),
900 doc_link: None,
901 },
902 GraphRAGError::NotFound { resource, id } => ErrorSuggestion {
903 action: format!("{} '{}' not found. Verify it exists.", resource, id),
904 code_example: None,
905 doc_link: None,
906 },
907 GraphRAGError::AlreadyExists { resource, id } => ErrorSuggestion {
908 action: format!("{} '{}' already exists. Use a different ID or update existing.", resource, id),
909 code_example: None,
910 doc_link: None,
911 },
912 _ => ErrorSuggestion {
914 action: "Check the error message for details".to_string(),
915 code_example: None,
916 doc_link: None,
917 },
918 }
919 }
920
921 pub fn display_with_suggestion(&self) -> String {
925 let suggestion = self.suggestion();
926 let mut output = format!("{}\n\n💡 Suggestion: {}", self, suggestion.action);
927 if let Some(code) = &suggestion.code_example {
928 output.push_str(&format!("\n\nExample:\n```rust\n{}\n```", code));
929 }
930 output
931 }
932}
933
934impl From<regex::Error> for GraphRAGError {
935 fn from(err: regex::Error) -> Self {
936 GraphRAGError::Validation {
937 message: format!("Regex error: {err}"),
938 }
939 }
940}
941
942#[cfg(test)]
943mod tests {
944 use super::*;
945
946 #[test]
947 fn test_error_display() {
948 let error = GraphRAGError::Config {
949 message: "Invalid configuration".to_string(),
950 };
951 assert_eq!(
952 format!("{error}"),
953 "Configuration error: Invalid configuration. Solution: Check your config file or use default settings with GraphRAG::builder()"
954 );
955 }
956
957 #[test]
958 fn test_error_context() {
959 let result: std::result::Result<(), std::io::Error> = Err(std::io::Error::new(
960 std::io::ErrorKind::NotFound,
961 "file not found",
962 ));
963
964 let error = result.with_context("loading configuration").unwrap_err();
965 assert!(matches!(error, GraphRAGError::Io(_)));
966 }
967
968 #[test]
969 fn test_error_macros() {
970 let error = config_error!("test message");
971 assert!(matches!(error, GraphRAGError::Config { .. }));
972
973 let error = storage_error!("test {} {}", "formatted", "message");
974 assert!(matches!(error, GraphRAGError::Storage { .. }));
975 }
976
977 #[test]
978 fn test_error_severity() {
979 let config_error = GraphRAGError::Config {
980 message: "test".to_string(),
981 };
982 assert_eq!(config_error.severity(), ErrorSeverity::Critical);
983 assert!(!config_error.is_recoverable());
984
985 let warning_error = GraphRAGError::Retrieval {
986 message: "test".to_string(),
987 };
988 assert_eq!(warning_error.severity(), ErrorSeverity::Warning);
989 assert!(warning_error.is_recoverable());
990 }
991
992 #[test]
993 fn test_error_suggestion_not_initialized() {
994 let error = GraphRAGError::NotInitialized;
995 let suggestion = error.suggestion();
996
997 assert!(suggestion.action.contains("Initialize"));
998 assert!(suggestion.code_example.is_some());
999 let code = suggestion.code_example.unwrap();
1000 assert!(code.contains("initialize()") || code.contains("quick_start"));
1001 }
1002
1003 #[test]
1004 fn test_error_suggestion_no_documents() {
1005 let error = GraphRAGError::NoDocuments;
1006 let suggestion = error.suggestion();
1007
1008 assert!(suggestion.action.contains("Add documents"));
1009 assert!(suggestion.code_example.is_some());
1010 let code = suggestion.code_example.unwrap();
1011 assert!(code.contains("add_document"));
1012 }
1013
1014 #[test]
1015 fn test_error_suggestion_config() {
1016 let error = GraphRAGError::Config {
1017 message: "File not found".to_string(),
1018 };
1019 let suggestion = error.suggestion();
1020
1021 assert!(suggestion.code_example.is_some());
1022 let code = suggestion.code_example.unwrap();
1023 assert!(code.contains("Config::default()") || code.contains("Config::load"));
1024 }
1025
1026 #[test]
1027 fn test_error_suggestion_not_found() {
1028 let error = GraphRAGError::NotFound {
1029 resource: "Document".to_string(),
1030 id: "test-123".to_string(),
1031 };
1032 let suggestion = error.suggestion();
1033
1034 assert!(suggestion.action.contains("Document"));
1035 assert!(suggestion.action.contains("test-123"));
1036 }
1037
1038 #[test]
1039 fn test_display_with_suggestion() {
1040 let error = GraphRAGError::NotInitialized;
1041 let display = error.display_with_suggestion();
1042
1043 assert!(display.contains("not initialized"));
1044 assert!(display.contains("Suggestion:"));
1045 assert!(display.contains("```rust"));
1046 }
1047}