zeroclaw-labs/zeroclaw · error
subgraph depth must be greater than 0
Error message
subgraph depth must be greater than 0
What it means
get_subgraph(root_id, depth) walks the graph outward from a root via a recursive CTE and treats depth as 'how many hops to include', so 0 would select nothing. It validates depth > 0 up front and bails with this message; after validation the depth is internally clamped to MAX_SUBGRAPH_DEPTH (100), so 0 is the only invalid value — anything from 1 upward is accepted.
Source
Thrown at crates/zeroclaw-memory/src/knowledge_graph.rs:549
while let Some(row) = rows.next()? {
results.push(row_to_node(row)?);
}
Ok(results)
}
/// Maximum allowed subgraph traversal depth.
const MAX_SUBGRAPH_DEPTH: usize = 100;
/// Extract a subgraph starting from `root_id` up to `depth` hops.
/// `depth` must be between 1 and `MAX_SUBGRAPH_DEPTH` (100).
/// Uses a recursive CTE for efficient single-query bidirectional traversal.
pub fn get_subgraph(
&self,
root_id: &str,
depth: usize,
) -> anyhow::Result<(Vec<KnowledgeNode>, Vec<KnowledgeEdge>)> {
if depth == 0 {
anyhow::bail!("subgraph depth must be greater than 0");
}
let depth = depth.min(Self::MAX_SUBGRAPH_DEPTH);
let conn = self.conn.lock();
// Collect reachable node IDs via recursive CTE (bidirectional traversal).
let mut node_stmt = conn.prepare(
"WITH RECURSIVE reachable(id, depth) AS (
SELECT ?1, 0
UNION
SELECT CASE WHEN e.from_id = r.id THEN e.to_id ELSE e.from_id END, r.depth + 1
FROM reachable r
JOIN edges e ON e.from_id = r.id OR e.to_id = r.id
WHERE r.depth < ?2
)
SELECT DISTINCT n.id, n.node_type, n.title, n.content, n.tags,
n.created_at, n.updated_at, n.source_project
FROM reachable rc
JOIN nodes n ON n.id = rc.id",View on GitHub (pinned to 88bb9c8533)
Solutions
- Pass at least 1: depth 1 means 'the root plus its direct neighbors'.
- If the value is computed, clamp it before the call: let depth = depth.clamp(1, KnowledgeGraph::MAX_SUBGRAPH_DEPTH).
- If 0 was meant as 'no limit', pass 100 (MAX_SUBGRAPH_DEPTH) instead — the implementation clamps to exactly that.
- Guard loops that decrement depth so they stop at 1 rather than calling with 0.
Example fix
// before let (nodes, edges) = graph.get_subgraph(&root, remaining_depth)?; // remaining_depth == 0 -> bail // after let depth = remaining_depth.max(1); // 0 hops is meaningless; 1 = root + direct neighbors let (nodes, edges) = graph.get_subgraph(&root, depth)?;
Defensive patterns
Strategy: validation
Validate before calling
// Clamp computed depths before the call let depth = depth.clamp(1, 100); // 1..=KnowledgeGraph::MAX_SUBGRAPH_DEPTH let (nodes, edges) = graph.get_subgraph(&root_id, depth)?;
Type guard
pub fn valid_subgraph_depth(depth: usize) -> bool {
depth >= 1 // values above 100 are clamped internally, only 0 is invalid
} Try / catch
// Expansion loops: treat 0 as 'direct neighbors only'
let depth = if remaining == 0 { 1 } else { remaining };
match graph.get_subgraph(&root_id, depth) {
Ok(sub) => render(sub),
Err(e) if e.to_string().contains("depth must be greater than 0") => {
render(graph.get_subgraph(&root_id, 1)?)
}
Err(e) => return Err(e),
} Prevention
- Map UI levels to hops explicitly (level N -> depth N, never N-1).
- Stop decrementing loops at 1; 0 remaining hops means 'stop', not 'query with 0'.
- Express 'unlimited' as MAX_SUBGRAPH_DEPTH (100), not 0 — the implementation clamps down to 100 from above.
- Assert depth >= 1 in debug builds where the value is computed.
When it happens
Trigger: Calling get_subgraph with depth == 0, most often because a caller-computed depth (levels minus one, a remaining-depth variable in a loop, or a config default of 0) underflowed or was never set. Hit directly or via client-facing wrappers that forward a user-supplied depth (client_relationship_types_roundtrip_through_queries tests exercise the same path).
Common situations: UI code mapping 'current level' to hops as level-1 and rendering level 0; pagination/expansion loops that reach 0 remaining hops and call instead of stopping; configs defaulting depth to 0 meaning 'unlimited' (here unlimited is expressed by any value >= 100 via the clamp, not 0).
Related errors
- unknown {}: {other}
- knowledge graph node limit reached ({}/{})
- tag '{}' contains a comma, which is used as the tag separato
- source node not found: {from_id}
- target node not found: {to_id}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/f24e65add726fb49.
Report an issue: GitHub.