{"record":{"id":"1e1e15c603732753","repo":"chroma-core/chroma","slug":"sum-requires-at-least-one-rank-expression","errorCode":null,"errorMessage":"Sum requires at least one rank expression","messagePattern":"Sum requires at least one rank expression","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"clients/new-js/packages/chromadb/src/execution/expression/rank.ts","lineNumber":505,"sourceCode":"      );\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 => {\n  if (inputs.length === 0) {\n    throw new Error(\"Sum requires at least one rank expression\");\n  }\n  const expressions = inputs.map((rank, index) =>\n    requireRank(rank, `Sum operand ${index}`),\n  );\n  return SumRankExpression.create(expressions);\n};\n\nexport const Sub = (left: RankInput, right: RankInput): RankExpression =>\n  new SubRankExpression(\n    requireRank(left, \"Sub left\"),\n    requireRank(right, \"Sub right\"),\n  );\n\nexport const Mul = (...inputs: RankInput[]): RankExpression => {\n  if (inputs.length === 0) {\n    throw new Error(\"Mul requires at least one rank expression\");\n  }\n  const expressions = inputs.map((rank, index) =>","sourceCodeStart":487,"sourceCodeEnd":523,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/new-js/packages/chromadb/src/execution/expression/rank.ts#L487-L523","documentation":"Thrown by the Sum rank factory (rank.ts:505) when called with zero arguments. Sum is variadic — Sum(a, b, ...) — and needs at least one operand to build the {$sum: [...]} JSON. Note the asymmetry: the method form expr.add() with no arguments returns expr unchanged, but the top-level Sum() factory with no arguments throws.","triggerScenarios":"Calling Sum() directly; spreading a possibly-empty array: Sum(...parts) where parts.length === 0. Common with dynamically collected scoring components where every component was filtered out or disabled.","commonSituations":"Aggregating conditionally-built score components behind feature flags; list-producing queries that return no components (empty result set, all filtered); refactors that moved a guaranteed operand into an optional variable.","solutions":["Ensure at least one operand: Sum(a, b) or Sum(...parts) with parts.length >= 1","Use a neutral identity for the empty case: Sum(...(parts.length ? parts : [0]))","Skip building the expression entirely when there is nothing to sum"],"exampleFix":"// before\nconst rank = Sum(...components); // throws when components is []\n\n// after\nconst rank = Sum(...(components.length ? components : [0]));\n// or: const rank = components.length ? Sum(...components) : Val(0);","handlingStrategy":"validation","validationCode":"const rank = components.length\n  ? Sum(...components)\n  : Val(0); // additive identity","typeGuard":null,"tryCatchPattern":"try {\n  const rank = Sum(...components);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Sum requires')) {\n    return Val(0);\n  }\n  throw e;\n}","preventionTips":["Check list length before spreading into variadic factories","Note expr.add() with no args is a no-op; only the Sum() factory throws","Use Val(0) as the additive identity for empty aggregations"],"tags":["sum","rank-expression","empty-arguments","input-validation"],"backgroundTag":"missing-required-argument","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}