{"record":{"id":"20ece8626f4ccdea","repo":"zeroclaw-labs/zeroclaw","slug":"source-node-not-found-from-id","errorCode":null,"errorMessage":"source node not found: {from_id}","messagePattern":"source node not found: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-memory/src/knowledge_graph.rs","lineNumber":268,"sourceCode":"        Ok(id)\n    }\n\n    /// Add a directed edge between two nodes.\n    pub fn add_edge(&self, from_id: &str, to_id: &str, relation: Relation) -> anyhow::Result<()> {\n        let conn = self.conn.lock();\n\n        // Verify both endpoints exist.\n        let exists = |id: &str| -> anyhow::Result<bool> {\n            let c: usize = conn.query_row(\n                \"SELECT COUNT(*) FROM nodes WHERE id = ?1\",\n                params![id],\n                |r| r.get(0),\n            )?;\n            Ok(c > 0)\n        };\n\n        if !exists(from_id)? {\n            anyhow::bail!(\"source node not found: {from_id}\");\n        }\n        if !exists(to_id)? {\n            anyhow::bail!(\"target node not found: {to_id}\");\n        }\n\n        conn.execute(\n            \"INSERT OR IGNORE INTO edges (from_id, to_id, relation) VALUES (?1, ?2, ?3)\",\n            params![from_id, to_id, relation.as_str()],\n        )?;\n\n        Ok(())\n    }\n\n    /// Retrieve a node by id.\n    pub fn get_node(&self, id: &str) -> anyhow::Result<Option<KnowledgeNode>> {\n        let conn = self.conn.lock();\n        let mut stmt = conn.prepare(\n            \"SELECT id, node_type, title, content, tags, created_at, updated_at, source_project","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-memory/src/knowledge_graph.rs#L250-L286","documentation":"add_edge inserts into an edges table whose from_id/to_id have foreign keys to nodes(id) (ON DELETE CASCADE), but before inserting it explicitly checks both endpoints with SELECT COUNT(*) FROM nodes WHERE id = ?. If the source (from_id) row does not exist, it bails with this message naming the missing id. This is a referential-integrity rejection: edges may only connect nodes present in this same SQLite file.","triggerScenarios":"Calling add_edge (or the relate handler) with a from_id that was never created by add_node on this graph: a hand-typed or truncated UUID, an id returned by a different KnowledgeGraph/database file, or an id belonging to a node deleted earlier in the same flow (its edges cascade-deleted, and the node itself is gone).","commonSituations":"Persisting node ids in another store (notes, tickets) and reusing them against a rebuilt/moved graph DB; hardcoding ids in scripts; races where a capture's node was rolled back or pruned before the relate step; splitting capture and relate across processes pointed at different db paths.","solutions":["Create the source node first (keep the id add_node returned) and pass that exact id to add_edge.","If the id came from elsewhere, verify it with get_node(from_id) — returning None confirms this error is imminent.","If the graph database was recreated or migrated, re-register the node under a new id and update references before relating.","Treat capture+relate as one unit: relate immediately after capture with the freshly returned id."],"exampleFix":"// before\ngraph.add_edge(\"00000000-0000-0000-0000-000000000000\", &expert_id, Relation::AuthoredBy)?; // source node not found\n\n// after: use the id the graph itself issued\nlet pattern_id = graph.add_node(NodeType::Pattern, \"t\", \"c\", &[], None)?;\ngraph.add_edge(&pattern_id, &expert_id, Relation::AuthoredBy)?;","handlingStrategy":"validation","validationCode":"// Verify endpoints before connecting\nif graph.get_node(from_id)?.is_none() {\n    return Err(anyhow::anyhow!(\"cannot relate: source node {from_id} does not exist\"));\n}\ngraph.add_edge(from_id, to_id, relation)?;","typeGuard":"pub fn can_connect(graph: &KnowledgeGraph, from_id: &str, to_id: &str) -> bool {\n    graph.get_node(from_id).ok().flatten().is_some() && graph.get_node(to_id).ok().flatten().is_some()\n}","tryCatchPattern":"// On capture+relate flows, recreate the missing endpoint instead of dropping the relation\nmatch graph.add_edge(from_id, to_id, relation) {\n    Ok(()) => {}\n    Err(e) if e.to_string().contains(\"source node not found\") => {\n        let from_id = graph.add_node(node_type, title, content, tags, None)?;\n        graph.add_edge(&from_id, to_id, relation)?;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Only use ids returned by add_node on the same KnowledgeGraph instance; never hand-type or persist them across database rebuilds.","Pair capture and relate in one function so the freshly issued id flows straight into add_edge.","Treat hub nodes (experts, clients) as managed fixtures: verify/recreate them before relating.","When loading ids from external stores, validate with get_node first and report which endpoint is missing."],"tags":["rust","zeroclaw","knowledge-graph","edges","referential-integrity","validation"],"backgroundTag":"foreign-key-violation","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}