ruvnet/ruflo · error · Error
candidate must ingest at least one vector
Error message
candidate ${label} must ingest at least one vector What it means
Thrown by the agenticow_speculate handler (agenticow-speculate-tools.ts:161) when a candidate's ingest array is missing or empty. Each speculative candidate is materialized by ingesting its records into a private COW branch of the base memory; a candidate with nothing to ingest cannot be scored or usefully explored, so it is rejected up front with the offending candidate's validated label in the message.
Solutions
- Give every candidate at least one record: ingest: [{ vector: [0.1, ...], text: 'optional' }]
- Remove candidates that carry no data — a zero-ingest candidate has nothing to score
- Validate all candidates in one pass before calling the tool
Example fix
// before
const candidates = [
{ label: 'good', ingest: [{ vector: v1 }] },
{ label: 'empty' }, // throws: candidate empty must ingest at least one vector
];
await callTool('agenticow_speculate', { basePath: p, candidates });
// after
const candidates = [{ label: 'good', ingest: [{ vector: v1 }] }];
await callTool('agenticow_speculate', { basePath: p, candidates }); Defensive patterns
Strategy: validation
Validate before calling
const valid = candidates.every(
(c) => c && typeof c.label === 'string' && Array.isArray(c.ingest) && c.ingest.length > 0,
);
if (!valid) throw new TypeError('every candidate needs label + at least one ingest record'); Prevention
- Filter out data-less candidates before submitting — speculate has no 'branch-only' candidate concept
- Log per-candidate validation failures so you know which label was the offender
When it happens
Trigger: An agenticow_speculate candidates entry like {label: 'exp-a'} with no ingest key, or {label: 'exp-a', ingest: []}. The check is Array.isArray(c.ingest) && c.ingest.length > 0 per candidate — one bad entry fails the whole call.
Common situations: A 'branch-only' candidate that was never meant to add data (not supported — every candidate must ingest at least one vector); dynamically built candidates whose records were filtered out; copy-paste of a candidate template without filling ingest.
Related errors
- at least one candidate is required
- records must be a non-empty array of
- scoreBy='nearest' requires a probe vector
- each record requires a non-empty numeric vector
- label exceeds 256 chars
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/180e0453e22aeada.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/mcp-tools/agenticow-speculate-tools.ts:161
const rawCandidates = input.candidates as CandidateInput[];
if (!Array.isArray(rawCandidates) || rawCandidates.length === 0) {
throw new Error('at least one candidate is required');
}
if (scoreBy === 'nearest' && !probe) {
throw new Error("scoreBy='nearest' requires a probe vector");
}
// Build the generic {label, fn} candidates. Each fn ingests into its own
// branch handle, then (for 'nearest') probes it so we can score.
// Map validated label → explicit branchPath so the branchPath() resolver
// below is O(1) instead of re-scanning + re-validating rawCandidates per
// candidate (explore() calls branchPath once per candidate → was O(n²)).
const explicitBranchPaths = new Map<string, string>();
const candidates: SpeculativeCandidate<CandidateOutcome>[] = rawCandidates.map((c) => {
const label = validateLabel(String(c.label));
if (!Array.isArray(c.ingest) || c.ingest.length === 0) {
throw new Error(`candidate ${label} must ingest at least one vector`);
}
if (typeof c.branchPath === 'string' && c.branchPath) {
explicitBranchPaths.set(label, c.branchPath);
}
const records = c.ingest.map((r) => ({
...(Number.isInteger(r.id) ? { id: r.id as number } : {}),
vector: r.vector,
...(typeof r.text === 'string' ? { text: r.text } : {}),
}));
return {
label,
fn: (branch: any): CandidateOutcome => {
const res = branch.ingest(records);
const accepted = Number(res?.accepted ?? records.length);
let hits: Array<{ id: number; distance: number }> = [];
if (probe) {
hits = (branch.query(probe, k) || []).map((h: any) => ({
id: h.id,View on GitHub (pinned to fa13ee4ad6)