{"record":{"id":"92395237964775f3","repo":"ruvnet/ruflo","slug":"scoreby-nearest-requires-a-probe-vector","errorCode":null,"errorMessage":"scoreBy='nearest' requires a probe vector","messagePattern":"scoreBy='nearest' requires a probe vector","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/agenticow-speculate-tools.ts","lineNumber":149,"sourceCode":"      },\n      required: ['basePath', 'candidates'],\n    },\n    handler: async (input) => {\n      const api = await loadAgenticow();\n      if (!api) return degradedResult('agenticow-not-found');\n\n      const basePath = resolveMemoryPath(String(input.basePath));\n      const dimension = input.dimension as number | undefined;\n      const scoreBy = (input.scoreBy as string) === 'count' ? 'count' : 'nearest';\n      const k = Number.isInteger(input.k) && (input.k as number) > 0 ? (input.k as number) : 1;\n      const probe = Array.isArray(input.probe) ? (input.probe as number[]) : null;\n\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 } : {}),","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/mcp-tools/agenticow-speculate-tools.ts#L131-L167","documentation":"Thrown by the agenticow_speculate handler (agenticow-speculate-tools.ts:149) when scoring mode is 'nearest' but no probe vector was supplied. Each candidate branch is scored by querying it with the probe, so the probe is required to rank candidates in nearest mode. Critically, the handler normalizes scoreBy as `input.scoreBy === 'count' ? 'count' : 'nearest'` — 'nearest' is the DEFAULT, and anything that is not exactly the lowercase string 'count' becomes 'nearest'.","triggerScenarios":"Omitting scoreBy (defaults to 'nearest') without a probe; a typo like 'Count', 'counts', or 'NEAREST'; explicitly passing scoreBy:'nearest' without probe. probe must be an actual array — a non-array value is coerced to null and fails the check even though the key is present.","commonSituations":"Users who only want count-based scoring forget to set scoreBy:'count' exactly; probe sent as a JSON string or nested object instead of a flat number[]; probe vector of a different length than the base dimension (passes this check but fails downstream).","solutions":["Add probe: a flat number[] with the same dimension as the base memory: probe: [0.12, -0.43, ...]","Or set scoreBy to exactly 'count' (lowercase) when you want ingestion-count scoring with no probe","Check for typos in scoreBy — anything not exactly 'count' silently selects 'nearest'"],"exampleFix":"// before\nawait callTool('agenticow_speculate', {\n  basePath: 'memory/main.rvf',\n  candidates: [{ label: 'a', ingest: [{ vector: v1 }] }],\n  // scoreBy omitted -> defaults to 'nearest' -> throws\n});\n\n// after (option A: supply probe)\nawait callTool('agenticow_speculate', {\n  basePath: 'memory/main.rvf',\n  candidates: [{ label: 'a', ingest: [{ vector: v1 }] }],\n  scoreBy: 'nearest', probe: q,\n});\n// after (option B: count scoring, no probe)\nawait callTool('agenticow_speculate', {\n  basePath: 'memory/main.rvf',\n  candidates: [{ label: 'a', ingest: [{ vector: v1 }] }],\n  scoreBy: 'count',\n});","handlingStrategy":"validation","validationCode":"const scoreBy = args.scoreBy === 'count' ? 'count' : 'nearest'; // mirror the tool's normalization\nif (scoreBy === 'nearest') {\n  if (!Array.isArray(args.probe) || args.probe.length === 0) {\n    throw new TypeError(\"scoreBy='nearest' (the default) requires probe: number[]\");\n  }\n}","typeGuard":"const isNumberArray = (v: unknown): v is number[] => Array.isArray(v) && v.every((n) => typeof n === 'number');","tryCatchPattern":"try {\n  await callTool('agenticow_speculate', args);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('requires a probe vector')) {\n    args.scoreBy = 'count'; // graceful downgrade to count scoring\n    return callTool('agenticow_speculate', args);\n  }\n  throw e;\n}","preventionTips":["Set scoreBy explicitly every time — omitting it silently means 'nearest' and requires a probe","Spell 'count' exactly (lowercase); any other value becomes 'nearest'","Send probe as a flat number[] with the same dimension as the base memory"],"tags":["validation","agenticow","speculative","conditional-required","mcp"],"backgroundTag":"conditionally-required-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"}