{"record":{"id":"ef0a81e564633724","repo":"chroma-core/chroma","slug":"weights-must-be-non-negative-numbers","errorCode":null,"errorMessage":"Weights must be non-negative numbers","messagePattern":"Weights must be non-negative numbers","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"clients/new-js/packages/chromadb/src/execution/expression/rank.ts","lineNumber":479,"sourceCode":"  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  }\n\n  if (normalize) {\n    const total = weightValues.reduce((sum, value) => sum + value, 0);\n    if (total <= 0) {\n      throw new Error(\n        \"Weights must sum to a positive value when normalize=true\",\n      );\n    }\n    weightValues = weightValues.map((value) => value / total);\n  }\n\n  const terms = expressions.map((rank, index) => {\n    const weight = weightValues[index];\n    const numerator = Val(weight);\n    const denominator = rank.add(k);\n    return numerator.divide(denominator);\n  });","sourceCodeStart":461,"sourceCodeEnd":497,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/new-js/packages/chromadb/src/execution/expression/rank.ts#L461-L497","documentation":"Thrown by Rrf (rank.ts:479) when any element of the `weights` array is not a number or is negative. Weights scale each ranker's contribution, so they must be finite non-negative numbers (0 is allowed and effectively disables that ranker's contribution). This runs after the length check, so mismatched lengths throw first.","triggerScenarios":"Calling Rrf({ ranks: [a, b], weights: [1, -0.5] }) — a negative emphasis; weights: ['0.7', 0.3] — strings from JSON config; weights: [1, NaN] — NaN from a failed computation. Weights containing Infinity also throw, since typeof passes but the value fails the < 0 / type test only for NaN via typeof — NaN passes typeof 'number' but NaN < 0 is false, so NaN weights slip this check and poison the math; the guard reliably catches negatives and non-numbers.","commonSituations":"Weights typed as strings in YAML/JSON config; sign errors in rebalancing logic (subtracting instead of adding); percentages like -20 from misparsed user input; mixing weight formats ('70%' vs 0.7).","solutions":["Use non-negative numbers: Rrf({ ranks, weights: [0.7, 0.3] })","Parse and validate config weights before use: nums = String(cfg.w).split(',').map(Number) then check Number.isFinite && >= 0","Clamp suspect values: weights.map(w => Math.max(0, Number(w) || 0)) if a degraded default is acceptable"],"exampleFix":"// before\nconst fused = Rrf({ ranks, weights: cfg.weights }); // ['0.7','0.3'] from JSON\n\n// after\nconst weights = cfg.weights.map((w: unknown) => Number(w));\nconst fused = Rrf({ ranks, weights }); // [0.7, 0.3]","handlingStrategy":"validation","validationCode":"const ok = weights.every(\n  (w) => typeof w === 'number' && Number.isFinite(w) && w >= 0,\n);\nconst fused = Rrf({ ranks, weights: ok ? weights : undefined });","typeGuard":"const isNonNegativeFinite = (v: unknown): v is number =>\n  typeof v === 'number' && Number.isFinite(v) && v >= 0;","tryCatchPattern":"try {\n  const fused = Rrf({ ranks, weights });\n} catch (e) {\n  if (e instanceof TypeError && e.message.includes('non-negative')) {\n    return Rrf({ ranks, weights: weights.map(w => Math.max(0, Number(w) || 0)) });\n  }\n  throw e;\n}","preventionTips":["Map config strings to Number before passing","Clamp rebalanced weights: Math.max(0, w)","Note NaN passes this particular guard — keep your own Number.isFinite check"],"tags":["rrf","weights","negative-value","input-validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}