diff --git a/crates/mem-cli/src/handlers/unified_query.rs b/crates/mem-cli/src/handlers/unified_query.rs index 0a7ed00..0a7bb0e 100644 --- a/crates/mem-cli/src/handlers/unified_query.rs +++ b/crates/mem-cli/src/handlers/unified_query.rs @@ -203,7 +203,9 @@ async fn search_entities( ).await { Ok(r) => r, Err(e) => { - error!("Entity search failed: {}", e); + crate::metrics::ERROR_UNEXPECTED_QUERY.inc(); + crate::metrics::ERROR_UNEXPECTED_TOTAL.inc(); + error!("Unexpected error: entity search failed: {}", e); return crate::handlers::response_builder::internal_error(&format!("Search failed: {}", e)); } }; @@ -305,7 +307,9 @@ async fn search_edges( ).await { Ok(r) => r, Err(e) => { - error!("Edge search failed: {}", e); + crate::metrics::ERROR_UNEXPECTED_QUERY.inc(); + crate::metrics::ERROR_UNEXPECTED_TOTAL.inc(); + error!("Unexpected error: edge search failed: {}", e); return crate::handlers::response_builder::internal_error(&format!("Search failed: {}", e)); } }; @@ -367,7 +371,9 @@ async fn search_hybrid( ).await { Ok(r) => r, Err(e) => { - error!("Hybrid search failed: {}", e); + crate::metrics::ERROR_UNEXPECTED_QUERY.inc(); + crate::metrics::ERROR_UNEXPECTED_TOTAL.inc(); + error!("Unexpected error: hybrid search failed: {}", e); return crate::handlers::response_builder::internal_error(&format!("Search failed: {}", e)); } }; diff --git a/crates/mem-cli/src/http_server.rs b/crates/mem-cli/src/http_server.rs index 03b9b7e..d0bf9ec 100644 --- a/crates/mem-cli/src/http_server.rs +++ b/crates/mem-cli/src/http_server.rs @@ -581,7 +581,9 @@ async fn execute_ingest( HttpResponse::Accepted().json(response) } Err(e) => { - tracing::error!("DB error: {}", e); + crate::metrics::ERROR_UNEXPECTED_INGEST.inc(); + crate::metrics::ERROR_UNEXPECTED_TOTAL.inc(); + tracing::error!(user_id = body.project.as_str(), "Unexpected DB error during ingest: {}", e); HttpResponse::InternalServerError().json(json!({"error": "database_error"})) } } @@ -905,7 +907,9 @@ pub async fn query_handler( match query_temporal_graph(&state, ¶ms).await { Ok(response) => HttpResponse::Ok().json(response), Err(e) => { - tracing::error!("Temporal graph query failed: {}", e); + crate::metrics::ERROR_UNEXPECTED_QUERY.inc(); + crate::metrics::ERROR_UNEXPECTED_TOTAL.inc(); + tracing::error!(user_id = claims.sub.as_str(), "Unexpected error: temporal graph query failed: {}", e); HttpResponse::InternalServerError().json(json!({"error": "query_failed", "reason": e.to_string()})) } } diff --git a/crates/mem-cli/src/metrics.rs b/crates/mem-cli/src/metrics.rs index 84937b2..504c156 100644 --- a/crates/mem-cli/src/metrics.rs +++ b/crates/mem-cli/src/metrics.rs @@ -371,6 +371,16 @@ pub static ERROR_FORBIDDEN_CONTEXT: Counter = Counter::new( pub static ERROR_LOOKUP_FAILURE_CONTEXT: Counter = Counter::new( "memory_error_lookup_failure_context_total", "Context lookup failure"); +// Unexpected errors (unhandled 500s, panics, unknown failures) +pub static ERROR_UNEXPECTED_TOTAL: Counter = Counter::new( + "memory_error_unexpected_total", "Total unexpected/unhandled errors (500s)"); +pub static ERROR_UNEXPECTED_INGEST: Counter = Counter::new( + "memory_error_unexpected_ingest_total", "Unexpected errors during ingest"); +pub static ERROR_UNEXPECTED_QUERY: Counter = Counter::new( + "memory_error_unexpected_query_total", "Unexpected errors during query"); +pub static ERROR_UNEXPECTED_CONTEXT: Counter = Counter::new( + "memory_error_unexpected_context_total", "Unexpected errors during context"); + // Last error info (most recent error for debugging) pub static LAST_ERROR_TIMESTAMP: Gauge = Gauge::new( "memory_last_error_timestamp_seconds", "Unix timestamp of most recent error"); @@ -579,6 +589,10 @@ pub fn render_metrics() -> String { counter!(ERROR_AUTH_FAILURE_CONTEXT); counter!(ERROR_FORBIDDEN_CONTEXT); counter!(ERROR_LOOKUP_FAILURE_CONTEXT); + counter!(ERROR_UNEXPECTED_TOTAL); + counter!(ERROR_UNEXPECTED_INGEST); + counter!(ERROR_UNEXPECTED_QUERY); + counter!(ERROR_UNEXPECTED_CONTEXT); gauge!(LAST_ERROR_TIMESTAMP); out