{"record":{"id":"e32055064efe9bc8","repo":"affaan-m/ECC","slug":"context-graph-observation-summary-cannot-be-empty","errorCode":null,"errorMessage":"Context graph observation summary cannot be empty","messagePattern":"Context graph observation summary cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ecc2/src/session/store.rs","lineNumber":3385,"sourceCode":"    }\n\n    pub fn add_context_observation(\n        &self,\n        session_id: Option<&str>,\n        entity_id: i64,\n        observation_type: &str,\n        priority: ContextObservationPriority,\n        pinned: bool,\n        summary: &str,\n        details: &BTreeMap<String, String>,\n    ) -> Result<ContextGraphObservation> {\n        if observation_type.trim().is_empty() {\n            return Err(anyhow::anyhow!(\n                \"Context graph observation type cannot be empty\"\n            ));\n        }\n        if summary.trim().is_empty() {\n            return Err(anyhow::anyhow!(\n                \"Context graph observation summary cannot be empty\"\n            ));\n        }\n\n        let now = chrono::Utc::now().to_rfc3339();\n        let details_json = serde_json::to_string(details)?;\n        self.conn.execute(\n            \"INSERT INTO context_graph_observations (\n                session_id, entity_id, observation_type, priority, pinned, summary, details_json, created_at\n             ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)\",\n            rusqlite::params![\n                session_id,\n                entity_id,\n                observation_type.trim(),\n                priority.as_db_value(),\n                pinned as i64,\n                summary.trim(),\n                details_json,","sourceCodeStart":3367,"sourceCodeEnd":3403,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/ecc2/src/session/store.rs#L3367-L3403","documentation":"The observation write path also requires a non-empty (trimmed) summary; a blank summary raises `Context graph observation summary cannot be empty`. The summary is the human-readable payload of the observation, so storing an empty one is treated as invalid input.","triggerScenarios":"Recording an observation whose summary is \"\" or whitespace — e.g. summarizing an event that produced no text, or piping empty log messages into observations.","commonSituations":"Auto-summarizers returning empty output for empty diffs; log shippers forwarding empty lines; templates interpolating an unset variable into the summary.","solutions":["Skip recording observations that have no summary content.","Generate a fallback summary (e.g. event name + timestamp) when the source text is empty.","Validate at the boundary: trim and check non-empty before calling the store.","Fix the upstream producer so it never emits blank summaries."],"exampleFix":"// before\nstore.add_context_graph_observation(session_id, kind, priority, pinned, &generated, &details)?;\n// after\nlet summary = if generated.trim().is_empty() {\n    format!(\"{kind} at {}\", chrono::Utc::now().to_rfc3339())\n} else { generated.trim().to_string() };\nstore.add_context_graph_observation(session_id, kind, priority, pinned, &summary, &details)?;","handlingStrategy":"validation","validationCode":"const s = summary.trim();\nif (!s) return; // nothing to record\nawait api.addContextGraphObservation(sessionId, type, priority, pinned, s, details);","typeGuard":"function hasSummary(v) { return typeof v === 'string' && v.trim().length > 0; }","tryCatchPattern":"try {\n    store.add_context_graph_observation(session_id, &kind, priority, pinned, &summary, &details)?;\n} catch (e) {\n    if (String(e).includes('observation summary cannot be empty')) {\n        log::warn!(\"dropped observation with blank summary\");\n    } else throw e;\n}","preventionTips":["Skip observations whose source produced no text instead of storing empty ones.","Generate a descriptive fallback summary (kind + timestamp) for blank inputs.","Check summarizer output for emptiness before calling the store.","Trim summaries at the ingestion boundary consistently."],"tags":["context-graph","observation","validation","empty-field"],"backgroundTag":"empty-required-field","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}