zeroclaw-labs/zeroclaw · error
source node not found: {from_id}
Error message
source node not found: {from_id} What it means
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.
Source
Thrown at crates/zeroclaw-memory/src/knowledge_graph.rs:268
Ok(id)
}
/// 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_projectView on GitHub (pinned to 88bb9c8533)
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.
Example fix
// before
graph.add_edge("00000000-0000-0000-0000-000000000000", &expert_id, Relation::AuthoredBy)?; // source node not found
// after: use the id the graph itself issued
let pattern_id = graph.add_node(NodeType::Pattern, "t", "c", &[], None)?;
graph.add_edge(&pattern_id, &expert_id, Relation::AuthoredBy)?; Defensive patterns
Strategy: validation
Validate before calling
// Verify endpoints before connecting
if graph.get_node(from_id)?.is_none() {
return Err(anyhow::anyhow!("cannot relate: source node {from_id} does not exist"));
}
graph.add_edge(from_id, to_id, relation)?; Type guard
pub fn can_connect(graph: &KnowledgeGraph, from_id: &str, to_id: &str) -> bool {
graph.get_node(from_id).ok().flatten().is_some() && graph.get_node(to_id).ok().flatten().is_some()
} Try / catch
// On capture+relate flows, recreate the missing endpoint instead of dropping the relation
match graph.add_edge(from_id, to_id, relation) {
Ok(()) => {}
Err(e) if e.to_string().contains("source node not found") => {
let from_id = graph.add_node(node_type, title, content, tags, None)?;
graph.add_edge(&from_id, to_id, relation)?;
}
Err(e) => return Err(e),
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- target node not found: {to_id}
- unknown {}: {other}
- cloud_ops.iac_tools must not be empty when cloud_ops is enab
- gateway.path_prefix contains invalid character '{bad}'; only
- risk_profiles.{profile_alias}.shell_env_passthrough[{i}] is
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/20ece8626f4ccdea.
Report an issue: GitHub.