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

  1. Give every candidate at least one record: ingest: [{ vector: [0.1, ...], text: 'optional' }]
  2. Remove candidates that carry no data — a zero-ingest candidate has nothing to score
  3. 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

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


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)