{"record":{"id":"eaf993119951c716","repo":"chroma-core/chroma","slug":"weights-must-sum-to-a-positive-value-when-normaliz","errorCode":null,"errorMessage":"Weights must sum to a positive value when normalize=true","messagePattern":"Weights must sum to a positive value when normalize=true","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"clients/new-js/packages/chromadb/src/execution/expression/rank.ts","lineNumber":485,"sourceCode":"\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  });\n\n  const fused = terms.reduce((acc, term) => acc.add(term));\n  return fused.negate();\n};\n\nexport const Sum = (...inputs: RankInput[]): RankExpression => {","sourceCodeStart":467,"sourceCodeEnd":503,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/new-js/packages/chromadb/src/execution/expression/rank.ts#L467-L503","documentation":"Thrown by Rrf (rank.ts:485) when normalize=true and the weights sum to zero or less. Normalization divides each weight by the total, so a zero total would divide by zero; since negative weights are already rejected by the previous check, this fires in practice when all weights are exactly 0.","triggerScenarios":"Calling Rrf({ ranks: [a, b], weights: [0, 0], normalize: true }) — disabling every ranker while asking for normalized output; dynamic weight schedules (e.g. time-based decay) that hit all-zero; user sliders all set to 0 in a tuning UI.","commonSituations":"Retriever-mixing UIs where users can zero out every component; softmax/decay computations that underflow to 0; enabling normalize by default while weights come from a sparse config where unset means 0 rather than 1.","solutions":["Give at least one weight a positive value, or use defaults by omitting weights (all 1s)","Treat all-zero as 'no preference': drop weights and pass normalize: false","Guard before calling: if (normalize && weights.reduce((s, w) => s + w, 0) <= 0) use unweighted ranks"],"exampleFix":"// before\nconst fused = Rrf({ ranks, weights: mix, normalize: true }); // mix = [0, 0]\n\n// after\nconst total = mix.reduce((s, w) => s + w, 0);\nconst fused =\n  total > 0\n    ? Rrf({ ranks, weights: mix, normalize: true })\n    : Rrf({ ranks }); // uniform fallback","handlingStrategy":"validation","validationCode":"const total = weights.reduce((s, w) => s + w, 0);\nconst fused =\n  total > 0\n    ? Rrf({ ranks, weights, normalize: true })\n    : Rrf({ ranks }); // all-zero mix: use uniform","typeGuard":null,"tryCatchPattern":"try {\n  const fused = Rrf({ ranks, weights, normalize: true });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('positive value')) {\n    return Rrf({ ranks, weights, normalize: false });\n  }\n  throw e;\n}","preventionTips":["Treat an all-zero weight vector as 'no preference' and drop weights","Keep a guaranteed positive baseline weight in tuning UIs","Normalize totals in your own code before enabling normalize"],"tags":["rrf","weights","normalization","zero-division"],"backgroundTag":"invalid-argument-value","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}