{"record":{"id":"65c4a6448e3f00a4","repo":"chroma-core/chroma","slug":"rank-dict-cannot-be-empty","errorCode":null,"errorMessage":"Rank dict cannot be empty","messagePattern":"Rank dict cannot be empty","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"chromadb/execution/expression/operator.py","lineNumber":674,"sourceCode":"\n        Supports operators:\n        - {\"$val\": number} -> Val(number)\n        - {\"$knn\": {...}} -> Knn(...)\n        - {\"$sum\": [ranks]} -> rank1 + rank2 + ...\n        - {\"$sub\": {\"left\": ..., \"right\": ...}} -> left - right\n        - {\"$mul\": [ranks]} -> rank1 * rank2 * ...\n        - {\"$div\": {\"left\": ..., \"right\": ...}} -> left / right\n        - {\"$abs\": rank} -> abs(rank)\n        - {\"$exp\": rank} -> rank.exp()\n        - {\"$log\": rank} -> rank.log()\n        - {\"$max\": [ranks]} -> rank1.max(rank2).max(rank3)...\n        - {\"$min\": [ranks]} -> rank1.min(rank2).min(rank3)...\n        \"\"\"\n        if not isinstance(data, dict):\n            raise TypeError(f\"Expected dict for Rank, got {type(data).__name__}\")\n\n        if not data:\n            raise ValueError(\"Rank dict cannot be empty\")\n\n        if len(data) != 1:\n            raise ValueError(\n                f\"Rank dict must contain exactly one operator, got {len(data)}\"\n            )\n\n        op = next(iter(data.keys()))\n\n        if op == \"$val\":\n            value = data[\"$val\"]\n            if not isinstance(value, (int, float)):\n                raise TypeError(f\"$val requires a number, got {type(value).__name__}\")\n            return Val(value)\n\n        elif op == \"$knn\":\n            knn_data = data[\"$knn\"]\n            if not isinstance(knn_data, dict):\n                raise TypeError(f\"$knn requires a dict, got {type(knn_data).__name__}\")","sourceCodeStart":656,"sourceCodeEnd":692,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/execution/expression/operator.py#L656-L692","documentation":"An empty dict is not a valid rank expression: Rank.from_dict({}) raises ValueError because there is no operator key to dispatch. Every rank node must carry exactly one $-prefixed operator, so {} almost always means an absent optional rank was forwarded anyway, or a nested operand is empty.","triggerScenarios":"Search(rank={}); Rank.from_dict({}); nested empties like {'$sum': [{}, {'$val': 1}]} where the empty child recurses into Rank.from_dict({}).","commonSituations":"Optional rank config defaulted with {} (user_cfg.get('rank', {})) and passed unconditionally; templating that renders an empty expression when a variable is missing; 'clearing' a rank by assigning {} instead of None.","solutions":["Pass rank=None to disable ranking.","Use rank_cfg or None instead of {} when the config may be missing.","When building trees programmatically, never emit a node whose operands are absent."],"exampleFix":"# before\nrank_cfg = user_params.get('rank', {})   # missing -> {}\nSearch(rank=rank_cfg)                     # -> ValueError: cannot be empty\n\n# after\nrank_cfg = user_params.get('rank') or None  # {} also becomes None\nSearch(rank=rank_cfg)","handlingStrategy":"validation","validationCode":"rank_cfg = request.get('rank') or None   # {} becomes None\nSearch(rank=rank_cfg)","typeGuard":"def is_valid_rank_dict(node) -> bool:\n    return isinstance(node, dict) and len(node) == 1","tryCatchPattern":null,"preventionTips":["Normalize empty or missing optional expressions to None before building Search.","Default expression config values to None, never {}.","In code generators, skip emitting a node when its operands are absent."],"tags":["validation","valueerror","rank","expression","chromadb"],"backgroundTag":"empty-input-validation","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}