zeroclaw-labs/zeroclaw · error

target node not found: {to_id}

Error message

target node not found: {to_id}

What it means

The second half of add_edge's endpoint validation: after the source node check passes, it runs the same SELECT COUNT(*) FROM nodes WHERE id = ? for to_id and bails with this message when the target row is absent. The edges table would enforce this via its FK to nodes(id) anyway; the explicit pre-check yields a precise message naming the missing target id instead of a raw constraint error.

Source

Thrown at crates/zeroclaw-memory/src/knowledge_graph.rs:271

    /// Add a directed edge between two nodes.
    pub fn add_edge(&self, from_id: &str, to_id: &str, relation: Relation) -> anyhow::Result<()> {
        let conn = self.conn.lock();

        // Verify both endpoints exist.
        let exists = |id: &str| -> anyhow::Result<bool> {
            let c: usize = conn.query_row(
                "SELECT COUNT(*) FROM nodes WHERE id = ?1",
                params![id],
                |r| r.get(0),
            )?;
            Ok(c > 0)
        };

        if !exists(from_id)? {
            anyhow::bail!("source node not found: {from_id}");
        }
        if !exists(to_id)? {
            anyhow::bail!("target node not found: {to_id}");
        }

        conn.execute(
            "INSERT OR IGNORE INTO edges (from_id, to_id, relation) VALUES (?1, ?2, ?3)",
            params![from_id, to_id, relation.as_str()],
        )?;

        Ok(())
    }

    /// Retrieve a node by id.
    pub fn get_node(&self, id: &str) -> anyhow::Result<Option<KnowledgeNode>> {
        let conn = self.conn.lock();
        let mut stmt = conn.prepare(
            "SELECT id, node_type, title, content, tags, created_at, updated_at, source_project
             FROM nodes WHERE id = ?1",
        )?;

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Ensure the target node exists first: create it via add_node or verify with get_node(to_id) and recreate it if it was pruned.
  2. Pass the exact id add_node returned for the target, from the same graph instance/database.
  3. If hub nodes keep disappearing, exclude them from pruning (or re-add them at startup) so relates have stable anchors.
  4. When loading ids from external sources, validate both endpoints before add_edge and report which one is missing.

Example fix

// before
graph.add_edge(&pattern_id, "expert-123", Relation::AuthoredBy)?; // target node not found: expert-123

// after: look up or create the target node, then relate
let expert = graph.query_by_tags(&["hub:expert".into()])?.into_iter().next();
let expert_id = match expert { Some(n) => n.id, None => graph.add_node(NodeType::Expert, "Dana", "...", &[], None)? };
graph.add_edge(&pattern_id, &expert_id, Relation::AuthoredBy)?;
Defensive patterns

Strategy: validation

Validate before calling

// Verify the target before connecting
if graph.get_node(to_id)?.is_none() {
    return Err(anyhow::anyhow!("cannot relate: target node {to_id} does not exist"));
}
graph.add_edge(from_id, to_id, relation)?;

Type guard

pub fn target_exists(graph: &KnowledgeGraph, to_id: &str) -> bool {
    graph.get_node(to_id).ok().flatten().is_some()
}

Try / catch

// Missing hub targets: recreate the hub, then retry the edge
match graph.add_edge(from_id, to_id, relation) {
    Ok(()) => {}
    Err(e) if e.to_string().contains("target node not found") => {
        let to_id = graph.add_node(NodeType::Expert, to_id, "recreated hub", &[], None)?;
        graph.add_edge(from_id, &to_id, relation)?;
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling add_edge where to_id does not exist in this graph's nodes table: relating to a deleted/pruned node, a typo'd or truncated UUID, an id from another database file, or a to_id captured from an older run before the graph was recreated.

Common situations: Relating new nodes to 'hub' nodes (experts, clients, technologies) that were pruned by capacity or hygiene runs; ids loaded from external config or exports; the same mixed-database mistake as the source variant but surfacing on the target argument.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/a841f150ff305787. Report an issue: GitHub.