JuliusBrussee/caveman · error · Error
${where} causal slice node ${nodeID} is not a node of varian
Error message
${where} causal slice node ${nodeID} is not a node of variant ${sliceVariant.id} What it means
Every id in causal_slice.node_chain must be a node of the slice variant (validate-continuous-improvement.mjs:182-184). The chain is the backward slice through the baseline variant's own graph, so it may not route through nodes that variant does not have.
Source
Thrown at packages/shared/contracts/scripts/validate-continuous-improvement.mjs:183
selectedPerArm.set(trace.arm, (selectedPerArm.get(trace.arm) ?? 0) + 1);
}
for (const [arm, count] of selectedPerArm) {
if (count > 3) throw new Error(`${where} arm ${arm} carries ${count} representative traces, more than three`);
}
const slice = item.causal_slice;
const sliceVariant = variantsByID.get(slice.variant_id);
if (!sliceVariant) throw new Error(`${where} causal slice references a workflow variant that is not in this report`);
if (sliceVariant.id !== armVariants.get("baseline").id) throw new Error(`${where} causal slice is not taken from the baseline arm's variant`);
const sliceNodes = new Set(sliceVariant.nodes.map((node) => node.id));
if (slice.manifestation_node_id !== "" && !sliceNodes.has(slice.manifestation_node_id)) {
throw new Error(`${where} manifestation node ${slice.manifestation_node_id} is not a node of variant ${sliceVariant.id}`);
}
if (item.diagnosis.manifestation.node_id !== slice.manifestation_node_id) {
throw new Error(`${where} diagnosis manifestation node disagrees with its causal slice`);
}
for (const nodeID of slice.node_chain) {
if (!sliceNodes.has(nodeID)) throw new Error(`${where} causal slice node ${nodeID} is not a node of variant ${sliceVariant.id}`);
}
if (slice.manifestation_node_id !== "" && !slice.node_chain.includes(slice.manifestation_node_id)) {
throw new Error(`${where} causal slice omits its own manifestation node`);
}
const provenDependency = (edge) => edge.strength === "hard" && (edge.type === "data" || edge.type === "control") && edge.evidence_refs.length > 0;
const variantEdges = new Map(sliceVariant.edges.map((edge) => [`${edge.from} ${edge.to} ${edge.type}`, edge]));
for (const edge of slice.hard_edges) {
if (!provenDependency(edge)) throw new Error(`${where} hard chain carries an unproven edge ${edge.from}->${edge.to}`);
if (!sliceNodes.has(edge.from) || !sliceNodes.has(edge.to)) throw new Error(`${where} hard chain edge ${edge.from}->${edge.to} names a node outside variant ${sliceVariant.id}`);
if (!slice.node_chain.includes(edge.from) || !slice.node_chain.includes(edge.to)) {
throw new Error(`${where} hard chain edge ${edge.from}->${edge.to} is outside the slice's node chain`);
}
const source = variantEdges.get(`${edge.from} ${edge.to} ${edge.type}`);
if (!source || source.strength !== "hard") throw new Error(`${where} hard chain edge ${edge.from}->${edge.to} is not a hard edge of variant ${sliceVariant.id}`);
}
for (const edge of slice.unproven_adjacencies) {
// The whole point of the separate list: nothing in it may read as proven.
if (provenDependency(edge)) throw new Error(`${where} unproven adjacency ${edge.from}->${edge.to} is in fact a proven hard dependency`);View on GitHub (pinned to 766dce6b13)
Solutions
- Remove or correct each offending id so the chain only contains nodes of the baseline variant.
- After fixing, confirm the chain still ends at the manifestation node (error 618) and its hard edges still resolve (error 619).
- Re-run the validator.
Example fix
// before — variant has no node/review "node_chain": ["node/parse", "node/review", "node/exec"] // after "node_chain": ["node/parse", "node/exec"]
Defensive patterns
Strategy: validation
Validate before calling
const chainNodesOutsideVariant = (report) => {
const byID = new Map(report.workflow_variants.map((v) => [v.id, v]));
return report.cases.flatMap((c) => {
const nodes = new Set(byID.get(c.causal_slice.variant_id)?.nodes.map((n) => n.id) ?? []);
return c.causal_slice.node_chain.filter((id) => !nodes.has(id)).map((id) => `${c.id}:${id}`);
});
}; Try / catch
try {
execFileSync(process.execPath, [VALIDATOR, reportPath, spansPath]);
} catch (err) {
if (/causal slice node .* is not a node/.test(err.message)) {
failCI(`node_chain routes through a node the variant does not have: ${err.message}`);
} else throw err;
} Prevention
- Build node chains from the baseline variant's own node set, programmatically.
- After node renames, sweep node_chain, hard_edges and manifestation ids in one pass.
- Never hand-extend a chain with an id you have not checked against variant.nodes.
When it happens
Trigger: The chain was built from the alternative variant's node set; a node was renamed in the variant without updating the chain; the chain was hand-extended with a plausible node id that the variant never defined.
Common situations: Reusing a chain across variants of the same family; refactors that rename nodes; LLM- or template-assisted fixture authoring inventing node ids.
Related errors
- ${where} causal slice references a workflow variant that is
- ${where} manifestation node ${slice.manifestation_node_id} i
- ${where} causal slice omits its own manifestation node
- ${where} cohort references a task family that is not in this
- ${where} cohort arm ${arm.role} references a workflow varian
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/da1047bfd0963d83.
Report an issue: GitHub.