{"record":{"id":"88b63364d4327a87","repo":"chroma-core/chroma","slug":"sum-requires-at-least-2-ranks-got-len-ranks-dat","errorCode":null,"errorMessage":"$sum requires at least 2 ranks, got {len(ranks_data)}","messagePattern":"\\$sum requires at least 2 ranks, got (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"chromadb/execution/expression/operator.py","lineNumber":758,"sourceCode":"                    f\"$knn return_rank must be a boolean, got {type(return_rank).__name__}\"\n                )\n\n            return Knn(\n                query=query,\n                key=key,\n                limit=limit,\n                default=knn_data.get(\"default\"),\n                return_rank=return_rank,\n            )\n\n        elif op == \"$sum\":\n            ranks_data = data[\"$sum\"]\n            if not isinstance(ranks_data, (list, tuple)):\n                raise TypeError(\n                    f\"$sum requires a list, got {type(ranks_data).__name__}\"\n                )\n            if len(ranks_data) < 2:\n                raise ValueError(\n                    f\"$sum requires at least 2 ranks, got {len(ranks_data)}\"\n                )\n\n            ranks = [Rank.from_dict(r) for r in ranks_data]\n            result = ranks[0]\n            for r in ranks[1:]:\n                result = result + r\n            return result\n\n        elif op == \"$sub\":\n            sub_data = data[\"$sub\"]\n            if not isinstance(sub_data, dict):\n                raise TypeError(\n                    f\"$sub requires a dict with 'left' and 'right', got {type(sub_data).__name__}\"\n                )\n            if \"left\" not in sub_data or \"right\" not in sub_data:\n                raise ValueError(\"$sub requires 'left' and 'right' fields\")\n","sourceCodeStart":740,"sourceCodeEnd":776,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/execution/expression/operator.py#L740-L776","documentation":"Chroma's execution engine deserializes rank (reranking) expressions from JSON via Rank.from_dict. The $sum operator folds several rank expressions into one Sum, and the parser requires its payload to be a list with at least two rank dicts, because a Sum with fewer than two operands is meaningless (the serializer side only ever emits >= 2 operands after flattening). This ValueError fires when the $sum payload list has 0 or 1 elements.","triggerScenarios":"Calling Rank.from_dict({'$sum': [...]}) directly, or submitting a query whose rank expression contains {'$sum': []} or {'$sum': [<single rank dict>]}. Most often the list is built dynamically over a variable number of searches and collapses to one element at runtime.","commonSituations":"Hybrid/multi-query search code that fuses N KNN rankings with $sum where a filter reduces N to 1; hand-written rank-expression JSON that wraps a lone term in $sum 'for consistency'; test fixtures copied from a two-query example and trimmed to one.","solutions":["If there is only one rank, drop the $sum wrapper and pass the rank dict itself (e.g. {'$knn': {...}} instead of {'$sum': [{'$knn': {...}}]})","If building dynamically, branch on length: expr = ranks[0] if len(ranks) == 1 else {'$sum': ranks}","Build expressions with the Python operator API (rank1 + rank2 ... then .to_dict()) instead of hand-writing operator dicts, so the serializer emits valid shapes","Validate the $sum payload shape with a guard before calling Rank.from_dict or sending the query"],"exampleFix":"# before\nranks = [{\"$knn\": {\"query\": q, \"return_rank\": True}} for q in queries]  # may be length 1\nexpr = {\"$sum\": ranks}\n\n# after\nranks = [{\"$knn\": {\"query\": q, \"return_rank\": True}} for q in queries]\nexpr = ranks[0] if len(ranks) == 1 else {\"$sum\": ranks}","handlingStrategy":"validation","validationCode":"def valid_sum_expr(expr):\n    return (\n        isinstance(expr, dict) and set(expr) == {\"$sum\"}\n        and isinstance(expr[\"$sum\"], (list, tuple))\n        and len(expr[\"$sum\"]) >= 2\n    )\n\nif not valid_sum_expr(rank_expr):\n    raise ValueError(f\"invalid $sum expression: {rank_expr!r}\")","typeGuard":"def is_sum_expr(d) -> bool:\n    return (isinstance(d, dict) and set(d) == {\"$sum\"}\n            and isinstance(d[\"$sum\"], (list, tuple))\n            and len(d[\"$sum\"]) >= 2)","tryCatchPattern":"try:\n    rank = Rank.from_dict(rank_expr)\nexcept (TypeError, ValueError) as e:\n    raise ValueError(f\"invalid rank expression {rank_expr!r}: {e}\") from e","preventionTips":["Build rank expressions with Python operator overloading (r1 + r2).to_dict() instead of hand-writing $sum dicts","When fusing a dynamic number of ranks, special-case len == 1 to pass the rank unwrapped","Add a round-trip test: Rank.from_dict(expr.to_dict()) for every expression your code emits"],"tags":["chromadb","rank-expression","validation","deserialization","hybrid-search"],"backgroundTag":"query-expression-validation","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}