chroma-core/chroma · error · ChromaValueError

Expected operator expression to have one operator, but got $

Error message

Expected operator expression to have one operator, but got ${value}

What it means

When a field's value is an object, it is treated as an operator expression and must contain exactly one operator key. Combining two operators on one field — the classic range query { $gt: 1, $lt: 5 } — throws ChromaValueError because the expression has two keys. Range conditions must be split into separate clauses joined with $and.

Source

Thrown at clients/new-js/packages/chromadb/src/utils.ts:572

      throw new ChromaValueError(
        `Expected 'where' value to be a string, number, boolean, or an operator expression, but got ${value}`,
      );
    }

    if (key === "$and" || key === "$or") {
      if (Object.keys(value).length <= 1) {
        throw new ChromaValueError(
          `Expected 'where' value for $and or $or to be a list of 'where' expressions, but got ${value}`,
        );
      }

      value.forEach((w: Where) => validateWhere(w));
      return;
    }

    if (typeof value === "object") {
      if (Object.keys(value).length != 1) {
        throw new ChromaValueError(
          `Expected operator expression to have one operator, but got ${value}`,
        );
      }

      const [operator, operand] = Object.entries(value)[0];

      if (
        ["$gt", "$gte", "$lt", "$lte"].includes(operator) &&
        typeof operand !== "number"
      ) {
        throw new ChromaValueError(
          `Expected operand value to be a number for ${operator}, but got ${typeof operand}`,
        );
      }

      if (["$in", "$nin"].includes(operator) && !Array.isArray(operand)) {
        throw new ChromaValueError(
          `Expected operand value to be an array for ${operator}, but got ${operand}`,

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Split multi-operator fields: { $and: [{ year: { $gt: 2000 } }, { year: { $lt: 2010 } }] }.
  2. Keep exactly one operator per brace block for a field.
  3. For plain equality use the shorthand { field: value } instead of an operator object.

Example fix

// before
where: { year: { $gt: 2000, $lt: 2010 } }

// after
where: { $and: [{ year: { $gt: 2000 } }, { year: { $lt: 2010 } }] }
Defensive patterns

Strategy: validation

Validate before calling

const range = (field, min, max) => ({
  $and: [{ [field]: { $gt: min } }, { [field]: { $lt: max } }],
});
await collection.query({ queryTexts, where: range('year', 2000, 2010) });

Type guard

const isSingleOperatorExpr = (v: object) => Object.keys(v).length === 1 && Object.keys(v)[0].startsWith('$');

Try / catch

try {
  await collection.query({ queryTexts, where });
} catch (e) {
  if ((e as Error).message.includes('one operator')) {
    // split the multi-operator expression into $and clauses and retry
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: where: { year: { $gt: 2000, $lt: 2010 } }. Two operators in one brace block, e.g. { $contains: 'a', $eq: 'b' }. An empty operator object { field: {} } (0 keys).

Common situations: Porting range/date filters from Mongo or SQL BETWEEN syntax; assuming operator objects compose implicitly with AND inside one field expression.

Related errors


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