{"record":{"id":"83848b6eb9e62937","repo":"chroma-core/chroma","slug":"trying-to-update-spann-config-but-schema-has-hnsw","errorCode":null,"errorMessage":"Trying to update SPANN config but schema has HNSW","messagePattern":"Trying to update SPANN config but schema has HNSW","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"chromadb/api/collection_configuration.py","lineNumber":875,"sourceCode":"            hnsw_config = vector_index.config.hnsw\n            update_hnsw = configuration[\"hnsw\"]\n\n            # Only update fields that are present in the update\n            if \"ef_search\" in update_hnsw:\n                hnsw_config.ef_search = update_hnsw[\"ef_search\"]\n            if \"num_threads\" in update_hnsw:\n                hnsw_config.num_threads = update_hnsw[\"num_threads\"]\n            if \"batch_size\" in update_hnsw:\n                hnsw_config.batch_size = update_hnsw[\"batch_size\"]\n            if \"sync_threshold\" in update_hnsw:\n                hnsw_config.sync_threshold = update_hnsw[\"sync_threshold\"]\n            if \"resize_factor\" in update_hnsw:\n                hnsw_config.resize_factor = update_hnsw[\"resize_factor\"]\n\n        elif \"spann\" in configuration and configuration[\"spann\"] is not None:\n            # Update SPANN config\n            if vector_index.config.spann is None:\n                raise ValueError(\"Trying to update SPANN config but schema has HNSW\")\n\n            spann_config = vector_index.config.spann\n            update_spann = configuration[\"spann\"]\n\n            # Only update fields that are present in the update\n            if \"search_nprobe\" in update_spann:\n                spann_config.search_nprobe = update_spann[\"search_nprobe\"]\n            if \"ef_search\" in update_spann:\n                spann_config.ef_search = update_spann[\"ef_search\"]\n\n        # Update embedding function if present\n        if (\n            \"embedding_function\" in configuration\n            and configuration[\"embedding_function\"] is not None\n        ):\n            vector_index.config.embedding_function = configuration[\"embedding_function\"]\n\n    return schema","sourceCodeStart":857,"sourceCodeEnd":893,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/api/collection_configuration.py#L857-L893","documentation":"This ValueError inside update_schema_from_collection_configuration is raised when an update payload contains a spann block but the collection's vector_index.config.spann is None — i.e. the collection was created with the HNSW index. Mirroring error 356, modify cannot convert an HNSW collection to SPANN; only the existing index type's tunables may be updated.","triggerScenarios":"Creating a collection with configuration={'hnsw': {...}} (the default) and then calling collection.modify(configuration={'spann': {'search_nprobe': 16}}). The guard fires on the first vector_index whose config.spann is None.","commonSituations":"Evaluating SPANN on existing HNSW-backed collections by flipping the config; shared tuning scripts that send spann blocks to every collection; CI pipelines applying uniform updates across heterogeneous collections.","solutions":["For HNSW collections, send updates under 'hnsw' (ef_search, num_threads, batch_size, sync_threshold, resize_factor) instead of 'spann'","To adopt SPANN, create a new collection with a spann configuration and re-ingest the data","Branch update payloads on the collection's actual index type before calling modify"],"exampleFix":"// before\n# collection created with default hnsw index\ncollection.modify(configuration={'spann': {'search_nprobe': 16}})  # ValueError\n\n// after\ncollection.modify(configuration={'hnsw': {'ef_search': 128}})","handlingStrategy":"validation","validationCode":"def index_type_of(collection) -> str:\n    cfg = collection.configuration or {}\n    return 'spann' if cfg.get('spann') is not None else 'hnsw'\n\nupdate = {index_type_of(collection): {'ef_search': 100}}\ncollection.modify(configuration=update)","typeGuard":"def update_matches_index(update: dict, collection) -> bool:\n    cfg = collection.configuration or {}\n    if update.get('spann') is not None:\n        return cfg.get('spann') is not None\n    if update.get('hnsw') is not None:\n        return cfg.get('hnsw') is not None\n    return True","tryCatchPattern":"try:\n    collection.modify(configuration=update)\nexcept ValueError as e:\n    if 'Trying to update SPANN config but schema has HNSW' in str(e):\n        collection.modify(configuration={'hnsw': {'ef_search': 100}})\n    else:\n        raise","preventionTips":["Default collections are HNSW; verify before sending spann updates","To evaluate SPANN, provision a new collection for the experiment rather than modifying HNSW ones","Centralize 'which index does this collection use' logic instead of scattering assumptions"],"tags":["chroma","spann","hnsw","modify-collection","index-type-mismatch"],"backgroundTag":"index-type-mismatch","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}