chroma-core/chroma · error · Error

Invalid SPANN config provided

Error message

Invalid SPANN config provided

What it means

GroupBy.from_dict() (operator.py:1523-1525) requires the 'keys' field of a group_by dict to be a list or tuple, and raises TypeError for anything else. The keys name the metadata fields whose values define group membership (e.g. ["category"] or ["author", "genre"]) and are run through _strings_to_keys for iteration (operator.py:1533, operator.py:1334-1336), so a scalar string, dict, or None cannot be interpreted. Note this parser is stricter than Select.from_dict, which also accepts sets (operator.py:1289) — order matters for grouping precedence, so a set is not allowed here.

Source

Thrown at clients/js/packages/chromadb-core/src/CollectionConfiguration.ts:208

export function collectionConfigurationToJson(
  config: CollectionConfiguration,
): Record<string, any> {
  if (config.hnsw && config.spann) {
    throw new InvalidConfigurationError(
      "Cannot specify both 'hnsw' and 'spann' configurations.",
    );
  }
  let hnswConfig = config.hnsw;
  let spannConfig = config.spann;
  let ef = config.embedding_function;
  let efConfig = serializeEmbeddingFunction(ef);

  // Basic validation/casting attempt (already done in create/update, but maybe check types?)
  if (hnswConfig && typeof hnswConfig !== "object") {
    throw new Error("Invalid HNSW config provided");
  }
  if (spannConfig && typeof spannConfig !== "object") {
    throw new Error("Invalid SPANN config provided");
  }

  // Note: Validation (like validateCreateHnswConfig) is tied to creation/update actions
  // not necessarily to retrieving/displaying the existing config.

  return {
    hnsw: hnswConfig,
    spann: spannConfig,
    embedding_function: efConfig,
  };
}

export function collectionConfigurationToJsonStr(
  config: CollectionConfiguration,
): string {
  try {
    const jsonObj = collectionConfigurationToJson(config);
    return JSON.stringify(jsonObj);

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Wrap in a list: {"keys": ["category"], "aggregate": {...}}.
  2. Convert sets before embedding: {"keys": sorted(group_fields)} — sorted() also makes multi-field grouping deterministic.
  3. Ensure each element is a string naming a real metadata field on your records.
  4. Avoid explicit null; omit the field only if you also omit aggregate (then use {} for no grouping).

Example fix

// before
group_by = {"keys": "category", "aggregate": {"$min_k": {"keys": ["#score"], "k": 1}}}
# TypeError: GroupBy keys must be a list, got str

# after
group_by = {"keys": ["category"], "aggregate": {"$min_k": {"keys": ["#score"], "k": 1}}}
Defensive patterns

Strategy: type-guard

Validate before calling

def groupby_keys_is_list(payload: dict) -> bool:
    ks = payload.get("keys")
    return ks is None or isinstance(ks, (list, tuple))

Type guard

def has_list_keys(v: Any) -> TypeGuard[Dict[str, Any]]:
    return isinstance(v, dict) and isinstance(v.get("keys"), (list, tuple))

Try / catch

try:
    GroupBy.from_dict(payload)
except TypeError as e:
    if "keys must be a list" in str(e):
        ks = payload["keys"]
        payload["keys"] = [ks] if isinstance(ks, str) else list(ks)
    else:
        raise

Prevention

When it happens

Trigger: {"keys": "category"} — single field not wrapped in a list; {"keys": {"0": "category"}} — object form from JSON; {"keys": null} — explicit null survives because the presence check ('keys' not in data) passed; forwarding a Python set from a Select payload as group_by keys.

Common situations: Single-field grouping written without brackets (the most common shape, hence the most common mistake); config loaders emitting objects instead of arrays; single-element arrays collapsed to scalars by intermediate serialization; reusing select-style key collections (sets) that this stricter parser refuses.

Related errors


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