Module: ClaudeMemory::MCP::ErrorClassifier
- Defined in:
- lib/claude_memory/mcp/error_classifier.rb
Overview
Classifies MCP tool errors into three tiers with structured responses.
Benign: Empty results, first use, database not yet initialized.
No error surfaced — return helpful guidance instead.
Retryable: Database locked, temporary I/O, connection reset.
Tool failed but may succeed on retry.
Fatal: Schema corruption, invalid parameters, programming bugs.
Requires user intervention to resolve.
Source: supermemory src/lib/error-helpers.js:1-72
Constant Summary collapse
- SEVERITY_BENIGN =
"benign"- SEVERITY_RETRYABLE =
"retryable"- SEVERITY_FATAL =
"fatal"- RETRYABLE_ERROR_NAMES =
Errors that indicate temporary/transient failures
%w[ Sequel::DatabaseConnectionError Extralite::BusyError ].freeze
- RETRYABLE_ERROR_CLASSES =
[ Errno::EACCES, Errno::EAGAIN, IOError ].freeze
- FATAL_ERROR_CLASSES =
Errors that indicate permanent/programming failures
[ Errno::ENOSPC, Errno::EROFS, TypeError, NoMethodError, ArgumentError ].freeze
Class Method Summary collapse
- .benign_message(reason) ⇒ Object
- .build_benign_response(reason, tool_name: nil) ⇒ Object
- .build_error_response(error, tool_name: nil) ⇒ Object
- .classify(error) ⇒ Object
- .database_error?(error) ⇒ Boolean
- .fatal?(error) ⇒ Boolean
- .fatal_message(error) ⇒ Object
- .fatal_recommendations(error) ⇒ Object
- .retryable?(error) ⇒ Boolean
- .retryable_message(error) ⇒ Object
Class Method Details
.benign_message(reason) ⇒ Object
157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/claude_memory/mcp/error_classifier.rb', line 157 def (reason) case reason when :no_results "No matching facts found. The memory system is working but has no relevant data for this query." when :not_initialized "Memory system not yet initialized. Facts will be stored as conversations happen." when :empty_database "Database exists but contains no facts yet. Use store_extraction to add facts." else "No data available." end end |
.build_benign_response(reason, tool_name: nil) ⇒ Object
83 84 85 86 87 88 89 90 |
# File 'lib/claude_memory/mcp/error_classifier.rb', line 83 def build_benign_response(reason, tool_name: nil) { severity: SEVERITY_BENIGN, message: (reason), tool: tool_name, results: [] } end |
.build_error_response(error, tool_name: nil) ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/claude_memory/mcp/error_classifier.rb', line 59 def build_error_response(error, tool_name: nil) severity = classify(error) case severity when SEVERITY_RETRYABLE { error: "Temporary failure", severity: SEVERITY_RETRYABLE, message: (error), tool: tool_name, retry: true } when SEVERITY_FATAL { error: "Operation failed", severity: SEVERITY_FATAL, message: (error), tool: tool_name, retry: false, recommendations: fatal_recommendations(error) } end end |
.classify(error) ⇒ Object
45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/claude_memory/mcp/error_classifier.rb', line 45 def classify(error) if retryable?(error) SEVERITY_RETRYABLE elsif fatal?(error) SEVERITY_FATAL elsif database_error?(error) # Generic database errors default to retryable SEVERITY_RETRYABLE else # Unknown errors default to fatal to surface issues SEVERITY_FATAL end end |
.database_error?(error) ⇒ Boolean
101 102 103 |
# File 'lib/claude_memory/mcp/error_classifier.rb', line 101 def database_error?(error) error.class.ancestors.any? { |a| a.name == "Sequel::DatabaseError" } end |
.fatal?(error) ⇒ Boolean
97 98 99 |
# File 'lib/claude_memory/mcp/error_classifier.rb', line 97 def fatal?(error) FATAL_ERROR_CLASSES.any? { |klass| error.is_a?(klass) } end |
.fatal_message(error) ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/claude_memory/mcp/error_classifier.rb', line 122 def (error) case error when Errno::ENOSPC "No disk space available for database operations." when Errno::EROFS "Database is on a read-only filesystem." when TypeError, NoMethodError "Internal error in memory system." when ArgumentError "Invalid parameters provided." else "Database error: #{error.}" end end |
.fatal_recommendations(error) ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/claude_memory/mcp/error_classifier.rb', line 137 def fatal_recommendations(error) recs = ["Run memory.check_setup to diagnose the issue"] case error when Errno::ENOSPC recs << "Free up disk space and retry" when Errno::EROFS recs << "Check filesystem permissions" else if error..include?("corrupt") || error..include?("malformed") recs << "Database may be corrupted — run: claude-memory doctor" recs << "If unrecoverable: claude-memory init --force" else recs << "If persistent: claude-memory doctor" end end recs end |
.retryable?(error) ⇒ Boolean
92 93 94 95 |
# File 'lib/claude_memory/mcp/error_classifier.rb', line 92 def retryable?(error) RETRYABLE_ERROR_CLASSES.any? { |klass| error.is_a?(klass) } || RETRYABLE_ERROR_NAMES.any? { |name| error.class.ancestors.any? { |a| a.name == name } } end |
.retryable_message(error) ⇒ Object
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/claude_memory/mcp/error_classifier.rb', line 105 def (error) case error when Errno::EACCES "Database file is temporarily inaccessible. Another process may hold the lock." when Errno::EAGAIN "Resource temporarily unavailable. Try again shortly." when IOError "I/O error during database access. Connection may have been interrupted." else if error.class.name&.include?("Busy") "Database is busy. Another operation is in progress." else "Temporary database error: #{error.}" end end end |