chroma-core/chroma · error · Error

Where dictionary must contain exactly one field

Error message

Where dictionary must contain exactly one field

What it means

Chroma's where grammar allows exactly one field (or one logical operator) per dictionary level: each dict is either { $and/$or: [...] } or { field: value }. A dictionary with zero or multiple field keys — like { category: 'news', status: 'active' } or {} — fails parsing with this error before any request is sent. Multiple conditions must be combined with $and/$or or the fluent .and() builder.

Source

Thrown at clients/new-js/packages/chromadb/src/execution/expression/where.ts:213

      if (!expr) {
        throw new TypeError(`Invalid where clause at index ${index}`);
      }
      return expr;
    });
    if (conditions.length === 1) {
      return conditions[0];
    }
    return conditions
      .slice(1)
      .reduce(
        (acc, condition) => OrWhere.combine(acc, condition),
        conditions[0],
      );
  }

  const entries = Object.entries(data);
  if (entries.length !== 1) {
    throw new Error("Where dictionary must contain exactly one field");
  }

  const [field, value] = entries[0];
  if (!isPlainObject(value)) {
    return new ComparisonWhere(field, "$eq", value);
  }

  const operatorEntries = Object.entries(value);
  if (operatorEntries.length !== 1) {
    throw new Error(
      `Operator dictionary for field "${field}" must contain exactly one operator`,
    );
  }

  const [operator, operand] = operatorEntries[0];
  const factory = comparisonOperatorMap.get(operator);
  if (!factory) {
    throw new Error(`Unsupported where operator: ${operator}`);

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Wrap each condition in its own dict and combine: { $and: [{ category: 'news' }, { status: 'active' }] }.
  2. Or use the builder: WhereExpression.from({ category: 'news' }).and({ status: 'active' }).
  3. Replace object-spread merging of filters with an array of clauses reduced into { $and: clauses }.
  4. Drop empty dicts ({}): skip filters that resolved to nothing instead of merging them in.

Example fix

// before
where: { ...categoryFilter, ...statusFilter } // { category: 'news', status: 'active' }

// after
where: { $and: [categoryFilter, statusFilter] } // one field per dict
Defensive patterns

Strategy: validation

Validate before calling

function toChromaWhere(filters: Record<string, unknown>[]): Record<string, unknown> | undefined {
  const clauses = filters.filter((f) => Object.keys(f).length === 1);
  if (clauses.length !== filters.length) {
    throw new Error('Each filter dict must contain exactly one field');
  }
  if (clauses.length === 0) return undefined;
  if (clauses.length === 1) return clauses[0];
  return { $and: clauses };
}

Type guard

function isSingleFieldDict(where: unknown): boolean {
  return (
    typeof where === 'object' &&
    where !== null &&
    !Array.isArray(where) &&
    Object.keys(where).length === 1
  );
}

Try / catch

try {
  await collection.query({ where });
} catch (e) {
  if (e instanceof Error && e.message === 'Where dictionary must contain exactly one field') {
    // split where into one-field dicts and rewrap as { $and: [...] }
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: where: { category: 'news', status: 'active' }; merging two valid one-field dicts with object spread ({ ...filterA, ...filterB }); where: {} (zero entries also fails the exactly-one check); pasting a MongoDB-style multi-field query verbatim.

Common situations: Combining independent filters with object spread — a JS idiom that works for Mongo-style APIs but violates Chroma's one-key-per-level grammar; migrating from the Python client or Mongo where multi-field dicts are legal; building filters from generic key/value config maps.

Related errors


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