{"record":{"id":"180e0453e22aeada","repo":"ruvnet/ruflo","slug":"candidate-label-must-ingest-at-least-one-vector","errorCode":null,"errorMessage":"candidate ${label} must ingest at least one vector","messagePattern":"candidate (.+?) must ingest at least one vector","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/agenticow-speculate-tools.ts","lineNumber":161,"sourceCode":"\n      const rawCandidates = input.candidates as CandidateInput[];\n      if (!Array.isArray(rawCandidates) || rawCandidates.length === 0) {\n        throw new Error('at least one candidate is required');\n      }\n      if (scoreBy === 'nearest' && !probe) {\n        throw new Error(\"scoreBy='nearest' requires a probe vector\");\n      }\n\n      // Build the generic {label, fn} candidates. Each fn ingests into its own\n      // branch handle, then (for 'nearest') probes it so we can score.\n      // Map validated label → explicit branchPath so the branchPath() resolver\n      // below is O(1) instead of re-scanning + re-validating rawCandidates per\n      // candidate (explore() calls branchPath once per candidate → was O(n²)).\n      const explicitBranchPaths = new Map<string, string>();\n      const candidates: SpeculativeCandidate<CandidateOutcome>[] = rawCandidates.map((c) => {\n        const label = validateLabel(String(c.label));\n        if (!Array.isArray(c.ingest) || c.ingest.length === 0) {\n          throw new Error(`candidate ${label} must ingest at least one vector`);\n        }\n        if (typeof c.branchPath === 'string' && c.branchPath) {\n          explicitBranchPaths.set(label, c.branchPath);\n        }\n        const records = c.ingest.map((r) => ({\n          ...(Number.isInteger(r.id) ? { id: r.id as number } : {}),\n          vector: r.vector,\n          ...(typeof r.text === 'string' ? { text: r.text } : {}),\n        }));\n        return {\n          label,\n          fn: (branch: any): CandidateOutcome => {\n            const res = branch.ingest(records);\n            const accepted = Number(res?.accepted ?? records.length);\n            let hits: Array<{ id: number; distance: number }> = [];\n            if (probe) {\n              hits = (branch.query(probe, k) || []).map((h: any) => ({\n                id: h.id,","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/mcp-tools/agenticow-speculate-tools.ts#L143-L179","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst candidates = [\n  { label: 'good', ingest: [{ vector: v1 }] },\n  { label: 'empty' }, // throws: candidate empty must ingest at least one vector\n];\nawait callTool('agenticow_speculate', { basePath: p, candidates });\n\n// after\nconst candidates = [{ label: 'good', ingest: [{ vector: v1 }] }];\nawait callTool('agenticow_speculate', { basePath: p, candidates });","handlingStrategy":"validation","validationCode":"const valid = candidates.every(\n  (c) => c && typeof c.label === 'string' && Array.isArray(c.ingest) && c.ingest.length > 0,\n);\nif (!valid) throw new TypeError('every candidate needs label + at least one ingest record');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["validation","agenticow","speculative","empty-array","mcp"],"backgroundTag":"empty-array-parameter","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}