{"record":{"id":"4592ca95fa958276","repo":"chroma-core/chroma","slug":"knn-requires-a-dict-got-type-knn-data-name","errorCode":null,"errorMessage":"$knn requires a dict, got {type(knn_data).__name__}","messagePattern":"\\$knn requires a dict, got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"chromadb/execution/expression/operator.py","lineNumber":692,"sourceCode":"            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__}\")\n\n            if \"query\" not in knn_data:\n                raise ValueError(\"$knn requires 'query' field\")\n\n            query = knn_data[\"query\"]\n\n            if isinstance(query, dict):\n                # SparseVector case - deserialize from transport format\n                if query.get(TYPE_KEY) == SPARSE_VECTOR_TYPE_VALUE:\n                    query = SparseVector.from_dict(query)\n                else:\n                    # Old format or invalid - try to construct directly\n                    raise ValueError(\n                        f\"Expected dict with {TYPE_KEY}='{SPARSE_VECTOR_TYPE_VALUE}', got {query}\"\n                    )\n\n            elif isinstance(query, (list, tuple, np.ndarray)):\n                # Dense vector case - normalize then validate","sourceCodeStart":674,"sourceCodeEnd":710,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/execution/expression/operator.py#L674-L710","documentation":"The $knn operator takes an options dict ({'query': ..., 'key': ..., 'limit': ..., 'default': ..., 'return_rank': ...}), not a bare vector. Passing the embedding list directly raises TypeError because the parser looks for the 'query' field inside a mapping before anything else.","triggerScenarios":"Search(rank={'$knn': [0.1, 0.2, 0.3]}); {'$knn': [[0.1], [0.2]]}; nested mistakes like {'$sum': [{'$knn': query_vector}, {'$val': 1}]} where the vector sits directly under $knn.","commonSituations":"Shorthand thinking ('the vector is the only argument'); refactoring code that passed query_embeddings=[...] to collection.query; hand-writing expressions from docs and dropping the wrapper level.","solutions":["Wrap the vector: {'$knn': {'query': [0.1, 0.2, 0.3]}}.","Or use the class: Search(rank=Knn(query=[0.1, 0.2, 0.3])).","Remember the other knobs (key, limit, default, return_rank) live beside 'query' in the same dict."],"exampleFix":"# before\nSearch(rank={'$knn': embedding})           # -> TypeError: got list\n\n# after\nSearch(rank={'$knn': {'query': embedding}})","handlingStrategy":"type-guard","validationCode":"def knn_node(query, **opts):\n    return {'$knn': {'query': list(query), **opts}}\n\nSearch(rank=knn_node(embedding, key='#embedding', limit=16))","typeGuard":"def is_knn_options(node) -> bool:\n    return (\n        isinstance(node, dict)\n        and 'query' in node\n        and set(node) <= {'query', 'key', 'limit', 'default', 'return_rank'}\n    )","tryCatchPattern":null,"preventionTips":["Always build $knn as {'$knn': {'query': ...}} - the vector never sits directly under $knn.","Prefer Knn(query=...) so the constructor shapes the payload.","Review nested trees: each $-operator has a different payload shape (list vs dict)."],"tags":["validation","typeerror","knn","rank","embedding","chromadb"],"backgroundTag":"type-validation-failed","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}