ruvnet/ruflo · error · Error

at least one candidate is required

Error message

at least one candidate is required

What it means

Thrown by the agenticow speculate tool handler when input.candidates is not an array or is empty. Speculation requires at least one candidate (each candidate ingests into its own branch and is scored against the probe); with zero candidates there is nothing to rank and the winner-promotion step is undefined, so the handler rejects early before touching the agenticow API.

Source

Thrown at v3/@claude-flow/cli/src/mcp-tools/agenticow-speculate-tools.ts:146

          description: 'ADR-171 fail-closed gate. When true, the top-scored winner is NOT promoted (base stays unchanged) and a provenance-tagged receipt is emitted — score alone cannot graduate work. Use when speculating over TASK outcomes rather than pure memory A/B. Default false (score-only promotion, tagged `unverified`).',
          default: false,
        },
      },
      required: ['basePath', 'candidates'],
    },
    handler: async (input) => {
      const api = await loadAgenticow();
      if (!api) return degradedResult('agenticow-not-found');

      const basePath = resolveMemoryPath(String(input.basePath));
      const dimension = input.dimension as number | undefined;
      const scoreBy = (input.scoreBy as string) === 'count' ? 'count' : 'nearest';
      const k = Number.isInteger(input.k) && (input.k as number) > 0 ? (input.k as number) : 1;
      const probe = Array.isArray(input.probe) ? (input.probe as number[]) : null;

      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);

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Pass a non-empty array of candidate objects, each with at least `label` and `ingest`.
  2. If your pipeline legitimately produces zero candidates, skip the speculate call rather than invoking with an empty array.
  3. Validate upstream: `Array.isArray(c) && c.length > 0` before calling the tool.

Example fix

// before
speculate({ basePath, candidates: [], scoreBy: 'nearest', probe: [0.1] }); // throws
// after
speculate({ basePath, candidates: [{ label: 'a', ingest: [{vector:[...]}] }], scoreBy: 'nearest', probe: [0.1] });
Defensive patterns

Strategy: validation

Validate before calling

function requireCandidates(c: unknown): Array<{ label: string; ingest: unknown[] }> {
  if (!Array.isArray(c) || c.length === 0) throw new Error('candidates must be a non-empty array');
  return c as Array<{ label: string; ingest: unknown[] }>;
}

Type guard

const isNonEmptyArray = (v: unknown): v is unknown[] => Array.isArray(v) && v.length > 0;

Try / catch

null

Prevention

When it happens

Trigger: Calling speculate without candidates; passing candidates as an empty array; passing a single object instead of an array; a pipeline that filtered out all candidates before invoking.

Common situations: Integrator built the candidates list conditionally and hit the empty branch; a fixture placeholder `candidates: []`; a serializer that returned undefined for the field; all candidates were filtered out by an upstream rule.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/9e8190755ae63357. Report an issue: GitHub.