{"record":{"id":"303efef38c582fd2","repo":"chroma-core/chroma","slug":"trying-to-update-hnsw-config-but-schema-has-spann","errorCode":null,"errorMessage":"Trying to update HNSW config but schema has SPANN","messagePattern":"Trying to update HNSW config but schema has SPANN","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"chromadb/api/collection_configuration.py","lineNumber":855,"sourceCode":"\n    embedding_value_types = schema.keys[embedding_key]\n    if (\n        embedding_value_types.float_list is None\n        or embedding_value_types.float_list.vector_index is None\n    ):\n        raise ValueError(\n            f\"Schema is missing keys[{embedding_key}].float_list.vector_index\"\n        )\n\n    # Update vector index config in both locations\n    for vector_index in [\n        schema.defaults.float_list.vector_index,\n        embedding_value_types.float_list.vector_index,\n    ]:\n        if \"hnsw\" in configuration and configuration[\"hnsw\"] is not None:\n            # Update HNSW config\n            if vector_index.config.hnsw is None:\n                raise ValueError(\"Trying to update HNSW config but schema has SPANN\")\n\n            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","sourceCodeStart":837,"sourceCodeEnd":873,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/api/collection_configuration.py#L837-L873","documentation":"This ValueError inside update_schema_from_collection_configuration is raised when an update payload contains an hnsw block but the collection's vector_index.config.hnsw is None — meaning the collection was created with the SPANN index (or has no HNSW index). Chroma does not convert a SPANN collection to HNSW via modify; the update must match the index type already in the schema.","triggerScenarios":"Creating a collection with configuration={'spann': {...}} and then calling collection.modify(configuration={'hnsw': {'ef_search': 100}}). The check runs for both copies of the vector index (defaults and '#embedding' key) and raises as soon as the first lacks hnsw.","commonSituations":"Teams tuning HNSW knobs against a collection that was actually provisioned with SPANN; config templates reused across collections with different index types; assuming modify can switch index types in place.","solutions":["Check which index type the collection uses (configuration/metadata) and send the matching block ('spann' updates for SPANN collections)","To move a SPANN collection to HNSW, create a new collection with an hnsw configuration and re-ingest the embeddings","Keep per-collection config templates keyed by index type to avoid cross-applied payloads"],"exampleFix":"// before\n# collection created with configuration={'spann': {'search_nprobe': 8}}\ncollection.modify(configuration={'hnsw': {'ef_search': 100}})  # ValueError\n\n// after\ncollection.modify(configuration={'spann': {'search_nprobe': 16, 'ef_search': 100}})","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('hnsw') is not None:\n        return cfg.get('hnsw') is not None\n    if update.get('spann') is not None:\n        return cfg.get('spann') is not None\n    return True","tryCatchPattern":"try:\n    collection.modify(configuration=update)\nexcept ValueError as e:\n    if 'Trying to update HNSW config but schema has SPANN' in str(e):\n        collection.modify(configuration={'spann': {'ef_search': update['hnsw']['ef_search']}})\n    else:\n        raise","preventionTips":["Derive the update block from the collection's actual index type, not a hardcoded one","Name config templates per index type (hnsw_tuning.yaml vs spann_tuning.yaml)","Remember modify tunes the existing index; it never converts index types"],"tags":["chroma","hnsw","spann","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"}