affaan-m/ECC · warning · anyhow::Error

Context graph observation type cannot be empty

Error message

Context graph observation type cannot be empty

What it means

Raised by add_context_observation in ecc2/src/session/store.rs:3380 when observation_type.trim().is_empty(). Observations are typed notes attached to a context-graph entity; the type drives filtering and display, so an empty type is rejected before the INSERT INTO context_graph_observations runs.

Source

Thrown at ecc2/src/session/store.rs:3380

        Ok(Some(ContextGraphEntityDetail {
            entity,
            outgoing,
            incoming,
        }))
    }

    pub fn add_context_observation(
        &self,
        session_id: Option<&str>,
        entity_id: i64,
        observation_type: &str,
        priority: ContextObservationPriority,
        pinned: bool,
        summary: &str,
        details: &BTreeMap<String, String>,
    ) -> Result<ContextGraphObservation> {
        if observation_type.trim().is_empty() {
            return Err(anyhow::anyhow!(
                "Context graph observation type cannot be empty"
            ));
        }
        if summary.trim().is_empty() {
            return Err(anyhow::anyhow!(
                "Context graph observation summary cannot be empty"
            ));
        }

        let now = chrono::Utc::now().to_rfc3339();
        let details_json = serde_json::to_string(details)?;
        self.conn.execute(
            "INSERT INTO context_graph_observations (
                session_id, entity_id, observation_type, priority, pinned, summary, details_json, created_at
             ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
            rusqlite::params![
                session_id,
                entity_id,

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Validate observation_type at the call site and require a known, non-empty value.
  2. If importing, skip rows with empty types or map them to a default like "note".
  3. Surface a UI-level required field for observation type.

Example fix

// before
store.add_context_observation(Some(sid), entity_id, "", priority, pinned, summary, &details)?;

// after: enforce a non-empty type
let observation_type = observation_type.trim();
if observation_type.is_empty() {
    anyhow::bail!("observation_type is required for entity {entity_id}");
}
store.add_context_observation(Some(sid), entity_id, observation_type, priority, pinned, summary, &details)?;
Defensive patterns

Strategy: validation

Validate before calling

// Validating newtype for observation_type.
#[derive(Debug, Clone)]
pub struct ObservationType(String);

impl ObservationType {
    pub fn new(raw: &str) -> anyhow::Result<Self> {
        let trimmed = raw.trim();
        if trimmed.is_empty() {
            anyhow::bail!("observation type cannot be empty");
        }
        Ok(Self(trimmed.to_string()))
    }
    pub fn as_str(&self) -> &str { &self.0 }
}

let otype = ObservationType::new(raw)?;
store.add_context_observation(sid, entity_id, otype.as_str(), priority, pinned, summary, &details)?;

Type guard

// ObservationType (above) is the type guard; it cannot represent an empty
// string, so any value satisfies the store's non-empty requirement.

Try / catch

for record in observation_import {
    let otype = match ObservationType::new(&record.observation_type) {
        Ok(t) => t,
        Err(_) => { tracing::warn!("skipping observation with empty type on entity {}", record.entity_id); continue; }
    };
    store.add_context_observation(sid, record.entity_id, otype.as_str(), record.priority, record.pinned, &record.summary, &record.details)?;
}

Prevention

When it happens

Trigger: Passing observation_type = ""; a caller that passes a raw enum variant name that was never set; importing observations from a source with blank type fields.

Common situations: Programmatic observation creation where the type is optional and defaulted to empty; bulk import pipelines with sparse columns.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/85dbca38b02397fc. Report an issue: GitHub.