chroma-core/chroma · error · ValueError

hnsw and spann cannot both be provided

Error message

hnsw and spann cannot both be provided

What it means

load_collection_configuration_from_json() refuses a configuration map in which both the 'spann' and 'hnsw' keys are non-null. A Chroma collection must use exactly one vector index backend, so a persisted or hand-built configuration naming both is rejected when the collection is loaded (get_collection) or when the JSON is parsed.

Source

Thrown at chromadb/api/collection_configuration.py:66

    embedding_function: Optional[EmbeddingFunction]  # type: ignore


def load_collection_configuration_from_json_str(
    config_json_str: str,
) -> CollectionConfiguration:
    config_json_map = json.loads(config_json_str)
    return load_collection_configuration_from_json(config_json_map)


# TODO: make warnings prettier and add link to migration docs
def load_collection_configuration_from_json(
    config_json_map: Dict[str, Any]
) -> CollectionConfiguration:
    if (
        config_json_map.get("spann") is not None
        and config_json_map.get("hnsw") is not None
    ):
        raise ValueError("hnsw and spann cannot both be provided")

    hnsw_config = None
    spann_config = None
    ef_config = None

    # Process vector index configuration (HNSW or SPANN)
    if config_json_map.get("hnsw") is not None:
        hnsw_config = cast(HNSWConfiguration, config_json_map["hnsw"])
    if config_json_map.get("spann") is not None:
        spann_config = cast(SpannConfiguration, config_json_map["spann"])

    # Process embedding function configuration
    if config_json_map.get("embedding_function") is not None:
        ef_config = config_json_map["embedding_function"]
        if ef_config["type"] == "legacy":
            warnings.warn(
                "legacy embedding function config",
                DeprecationWarning,

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Recreate the collection specifying only one index type in its configuration.
  2. If you control the dict, delete one of the two keys before passing it.
  3. Repair the stored configuration (remove the stray key from the collection's config JSON in the backing store) — back up data first.
  4. Report upstream if the stored both-keys state was produced by Chroma itself.

Example fix

# before
config_json = {"hnsw": {"space": "l2"}, "spann": {"search_nprobe": 16}}
load_collection_configuration_from_json(config_json)  # ValueError

# after
config_json = {"hnsw": {"space": "l2"}}
load_collection_configuration_from_json(config_json)
Defensive patterns

Strategy: validation

Validate before calling

def single_index_config(config: dict) -> dict:
    if config.get("hnsw") is not None and config.get("spann") is not None:
        raise ValueError("pick one: hnsw or spann")
    return config

config = single_index_config({"hnsw": {"space": "l2"}, "spann": None})  # ok

Try / catch

try:
    col = client.get_collection("docs")
except ValueError as e:
    if "cannot both be provided" in str(e):
        # stored config is corrupt: recreate the collection with one index type
        ...
    raise

Prevention

When it happens

Trigger: get_collection() on a collection whose stored configuration JSON contains both 'hnsw' and 'spann' (corrupt or manually edited data); building the configuration dict yourself with both keys; copying/modifying persisted SQLite rows when experimenting with SPANN migrations.

Common situations: Attempting an HNSW→SPANN migration by editing stored config; bugs in scripts that merge configuration JSONs; testing the newer SPANN index against existing HNSW collections.

Related errors


AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16). Data as JSON: /api/errors/8a4d7d9b0a3b8bc2. Report an issue: GitHub.