{"record":{"id":"78a964b71910826d","repo":"chroma-core/chroma","slug":"rrf-k-must-be-a-positive-integer","errorCode":null,"errorMessage":"Rrf k must be a positive integer","messagePattern":"Rrf k must be a positive integer","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"clients/new-js/packages/chromadb/src/execution/expression/rank.ts","lineNumber":462,"sourceCode":"\nexport const Knn = (options: KnnOptions): RankExpression =>\n  new KnnRankExpression(normalizeKnnOptions(options));\n\nexport interface RrfOptions {\n  ranks: RankInput[];\n  k?: number;\n  weights?: number[];\n  normalize?: boolean;\n}\n\nexport const Rrf = ({\n  ranks,\n  k = 60,\n  weights,\n  normalize = false,\n}: RrfOptions): RankExpression => {\n  if (!Number.isInteger(k) || k <= 0) {\n    throw new TypeError(\"Rrf k must be a positive integer\");\n  }\n  if (!Array.isArray(ranks) || ranks.length === 0) {\n    throw new TypeError(\"Rrf requires at least one rank expression\");\n  }\n\n  const expressions = ranks.map((rank, index) =>\n    requireRank(rank, `ranks[${index}]`),\n  );\n\n  let weightValues = weights\n    ? weights.slice()\n    : new Array(expressions.length).fill(1);\n  if (weightValues.length !== expressions.length) {\n    throw new Error(\"Number of weights must match number of ranks\");\n  }\n  if (weightValues.some((value) => typeof value !== \"number\" || value < 0)) {\n    throw new TypeError(\"Weights must be non-negative numbers\");\n  }","sourceCodeStart":444,"sourceCodeEnd":480,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/new-js/packages/chromadb/src/execution/expression/rank.ts#L444-L480","documentation":"Thrown by the Rrf (reciprocal rank fusion) factory in the Chroma JS client (rank.ts:462) when its `k` smoothing constant — which defaults to 60 — is not an integer greater than zero. k is used as rank.add(k) in the fusion formula 1/(rank+k), so 0 or fractional values are rejected at expression-build time, client-side, before any request. Note k is validated before `ranks`, so a bad k masks an empty-ranks problem.","triggerScenarios":"Calling Rrf({ ranks, k: 0 }), k: -1, k: 0.5, or k: NaN. Typical sources: k read from config/env without parsing (string '60' is not a number and fails Number.isInteger), or k computed as a float (e.g. ranks.length / 2 with an odd count).","commonSituations":"Tuning RRF smoothing from external config where the value arrives as a string; formulas that produce fractional constants; copying literature values like k=1.2 (fractional) which this client rejects; setting k=0 intending 'no smoothing', which would cause division by rank 0.","solutions":["Use an integer k >= 1, or omit k to keep the default of 60","If k comes from config, convert and validate: Number.isInteger(k) && k > 0, otherwise fall back to 60","Fix the upstream formula so it yields an integer (Math.round/Math.trunc)"],"exampleFix":"// before\nconst fused = Rrf({ ranks, k: Number(cfg.rrfK) }); // NaN or 0.5 throws\n\n// after\nconst rawK = Number(cfg.rrfK);\nconst fused = Rrf({ ranks, k: Number.isInteger(rawK) && rawK > 0 ? rawK : undefined });","handlingStrategy":"validation","validationCode":"const safeK = (v: unknown): number | undefined =>\n  typeof v === 'number' && Number.isInteger(v) && v > 0 ? v : undefined;\nconst fused = Rrf({ ranks, k: safeK(cfg.rrfK) }); // undefined -> default 60","typeGuard":"const isPositiveInteger = (v: unknown): v is number =>\n  typeof v === 'number' && Number.isInteger(v) && v > 0;","tryCatchPattern":"try {\n  const fused = Rrf({ ranks, k });\n} catch (e) {\n  if (e instanceof TypeError && e.message.includes('Rrf k')) {\n    return Rrf({ ranks }); // default k=60\n  }\n  throw e;\n}","preventionTips":["Convert config values: string '60' fails Number.isInteger","Round computed constants: Math.max(1, Math.round(k))","Remember k is validated before ranks — a bad k hides an empty-ranks problem"],"tags":["rrf","k","input-validation","rank-expression","client-side"],"backgroundTag":"invalid-argument-value","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}