{"record":{"id":"8b036463921a8f91","repo":"chroma-core/chroma","slug":"mul-requires-a-list-got-type-ranks-data-name","errorCode":null,"errorMessage":"$mul requires a list, got {type(ranks_data).__name__}","messagePattern":"\\$mul requires a list, got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"chromadb/execution/expression/operator.py","lineNumber":784,"sourceCode":"            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\n            left = Rank.from_dict(sub_data[\"left\"])\n            right = Rank.from_dict(sub_data[\"right\"])\n            return left - right\n\n        elif op == \"$mul\":\n            ranks_data = data[\"$mul\"]\n            if not isinstance(ranks_data, (list, tuple)):\n                raise TypeError(\n                    f\"$mul requires a list, got {type(ranks_data).__name__}\"\n                )\n            if len(ranks_data) < 2:\n                raise ValueError(\n                    f\"$mul 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 == \"$div\":\n            div_data = data[\"$div\"]\n            if not isinstance(div_data, dict):\n                raise TypeError(\n                    f\"$div requires a dict with 'left' and 'right', got {type(div_data).__name__}\"","sourceCodeStart":766,"sourceCodeEnd":802,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/execution/expression/operator.py#L766-L802","documentation":"The $mul rank operator multiplies a chain of rank expressions and, like $sum, expects its payload to be a list of operand dicts. Rank.from_dict raises this TypeError when the $mul payload is not a list/tuple — most commonly a {'left','right'} dict copied from the $sub/$div convention.","triggerScenarios":"Rank.from_dict({'$mul': {'left': a, 'right': b}}), or {'$mul': scalar}, nested anywhere in a rank expression tree being deserialized.","commonSituations":"Mixing up the two payload conventions in Chroma's rank DSL: $sum/$mul/$max/$min take lists while $sub/$div take left/right dicts; a generic expression builder that emits the dict shape for every binary operator; hand-edited JSON.","solutions":["Use the list form: {\"$mul\": [rank_a, rank_b, ...]}","If your builder produces {'left','right'} pairs, convert: {'$mul': [pair['left'], pair['right']]}","Build the expression in Python as a * b * c and call .to_dict()","Guard: isinstance(expr['$mul'], (list, tuple)) before deserialization"],"exampleFix":"# before\nexpr = {\"$mul\": {\"left\": knn_rank, \"right\": {\"$val\": 0.8}}}\n\n# after\nexpr = {\"$mul\": [knn_rank, {\"$val\": 0.8}]}","handlingStrategy":"validation","validationCode":"def valid_mul_expr(expr):\n    return (\n        isinstance(expr, dict) and set(expr) == {\"$mul\"}\n        and isinstance(expr[\"$mul\"], (list, tuple))\n    )\n\nif not valid_mul_expr(rank_expr):\n    raise ValueError(f\"$mul payload must be a list, got {rank_expr!r}\")","typeGuard":"def is_mul_expr(d) -> bool:\n    return (isinstance(d, dict) and set(d) == {\"$mul\"}\n            and isinstance(d[\"$mul\"], (list, tuple)))","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":["Keep a cheat sheet of payload shapes: multiplicative ops take operand lists, not left/right dicts","Generate weighted products in Python: (knn * 0.8).to_dict()","Unit-test every generated operator dict against Rank.from_dict"],"tags":["chromadb","rank-expression","validation","deserialization","type-error"],"backgroundTag":"query-expression-validation","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}