{"record":{"id":"c493185d562c0cf7","repo":"chroma-core/chroma","slug":"cannot-specify-both-hnsw-and-spann-configurati","errorCode":null,"errorMessage":"Cannot specify both 'hnsw' and 'spann' configurations.","messagePattern":"Cannot specify both 'hnsw' and 'spann' configurations\\.","errorType":"validation","errorClass":"InvalidConfigurationError","httpStatus":null,"severity":"error","filePath":"clients/js/packages/chromadb-core/src/CollectionConfiguration.ts","lineNumber":161,"sourceCode":"      return efBuilder.build_from_config(efConfig.config);\n    } catch (e) {\n      console.error(\"Error building embedding function from config:\", e);\n      return null; // Fallback if build fails\n    }\n  } else {\n    console.warn(\n      `Unknown embedding function type or name: ${efConfig.type}, ${efConfig.name}`,\n    );\n    return null;\n  }\n}\n\n// TODO: make warnings prettier and add link to migration docs\nexport function loadCollectionConfigurationFromJson(\n  jsonMap: Record<string, any>,\n): CollectionConfiguration {\n  if (jsonMap.hnsw && jsonMap.spann) {\n    throw new InvalidConfigurationError(\n      \"Cannot specify both 'hnsw' and 'spann' configurations.\",\n    );\n  }\n  let hnswConfig: HNSWConfiguration | null | undefined = jsonMap.hnsw; // Assume structure matches HNSWConfiguration\n  let spannConfig: SpannConfiguration | null | undefined = jsonMap.spann; // Assume structure matches SpannConfiguration\n  let embeddingFunction: IEmbeddingFunction | null | undefined =\n    deserializeEmbeddingFunction(jsonMap.embedding_function);\n\n  return {\n    hnsw: hnswConfig,\n    spann: spannConfig,\n    embedding_function: embeddingFunction,\n  };\n}\n\nexport function loadCollectionConfigurationFromJsonStr(\n  jsonStr: string,\n): CollectionConfiguration {","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/js/packages/chromadb-core/src/CollectionConfiguration.ts#L143-L179","documentation":"Aggregate.from_dict() (operator.py:1428-1429) raises ValueError when the single operator key in an aggregate dict is neither '$min_k' nor '$max_k'. These two strings are the complete operator vocabulary (dispatch at operator.py:1422-1427), so '$top_k', '$avg', '$sort', 'min_k' (missing the $), or any custom name is rejected. The error echoes the offending operator name, making typo-vs-invented-operator immediately distinguishable.","triggerScenarios":"'$top_k' — inventing a name for the familiar concept ('$min_k' on '#score' is Chroma's top-k by relevance, since lower score = better); '$avg'/'$sum' — expecting SQL-style aggregate functions that do not exist; 'min_k' without the '$' prefix; 'MinK' — using the class name; operators from a different Chroma version's vocabulary or a newer spec.","commonSituations":"Porting SQL/Mongo-style aggregation syntax to Chroma; paraphrasing operator names from memory instead of docs; version drift where a payload written against a richer (perhaps experimental or future) operator set is run on a build supporting only min/max k; LLM- or template-generated payloads with plausible-but-wrong operator names.","solutions":["Use exactly '$min_k' or '$max_k': {\"$min_k\": {\"keys\": [\"#score\"], \"k\": 3}} keeps the 3 best per group (min score = most similar).","Check the '$' prefix — 'min_k' without it is an unknown operator.","Do not expect avg/sum/count/sort operators; if you need them, aggregate client-side after the grouped search.","If a payload previously worked, verify the Chroma version — the supported set is defined solely by the from_dict dispatch."],"exampleFix":"// before\naggregate = {\"$top_k\": {\"keys\": [\"#score\"], \"k\": 3}}\n# ValueError: Unknown aggregate operator: $top_k\n\n# after\naggregate = {\"$min_k\": {\"keys\": [\"#score\"], \"k\": 3}}","handlingStrategy":"validation","validationCode":"SUPPORTED = {\"$min_k\", \"$max_k\"}\ndef aggregate_op_supported(payload: dict) -> bool:\n    return isinstance(payload, dict) and len(payload) == 1 and next(iter(payload)) in SUPPORTED","typeGuard":"def is_supported_aggregate(v: Any) -> TypeGuard[Dict[str, Dict[str, Any]]]:\n    if not (isinstance(v, dict) and len(v) == 1):\n        return False\n    op = next(iter(v))\n    return op in {\"$min_k\", \"$max_k\"} and isinstance(v[op], dict)","tryCatchPattern":"try:\n    Aggregate.from_dict(agg)\nexcept ValueError as e:\n    if \"Unknown aggregate operator\" in str(e):\n        raise ValueError(\n            \"Only '$min_k' and '$max_k' are supported (note the '$' prefix); \"\n            f\"got operator {next(iter(agg))!r}\"\n        ) from e\n    raise","preventionTips":["The vocabulary is exactly ['$min_k', '$max_k'] — top-k by relevance is $min_k on '#score'.","Keep the '$' prefix; 'min_k' bare is unknown.","Cross-check operator names against the running Chroma version after upgrades."],"tags":["chromadb","aggregate","unknown-operator","valueerror","min-k","max-k"],"backgroundTag":"unknown-operator-name","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}