chroma-core/chroma · error · ChromaValueError

Expected 'where' value for $and or $or to be a list of 'wher

Error message

Expected 'where' value for $and or $or to be a list of 'where' expressions, but got ${value}

What it means

When the top-level operator is $and or $or, validateWhere requires its value — an array of where expressions — to have more than one entry; Object.keys on the array yields indices, so length <= 1 throws. A zero- or one-element logical combination is meaningless in Chroma's grammar and is rejected. Each entry is then recursively validated as a full where clause.

Source

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

    );
  }

  Object.entries(where).forEach(([key, value]) => {
    if (
      key !== "$and" &&
      key !== "$or" &&
      key !== "$in" &&
      key !== "$nin" &&
      !["string", "number", "boolean", "object"].includes(typeof value)
    ) {
      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 (

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Use the single filter directly when the list has one entry: clauses.length === 1 ? clauses[0] : { $and: clauses }.
  2. Require at least two clauses for $and/$or.
  3. Always pass an array (not an object) as the $and/$or value.

Example fix

// before
const where = { $and: clauses }; // clauses has 0 or 1 entries

// after
const where = clauses.length > 1 ? { $and: clauses } : clauses[0];
Defensive patterns

Strategy: validation

Validate before calling

const buildLogical = (op, clauses) => {
  if (clauses.length > 1) return { [op]: clauses };
  if (clauses.length === 1) return clauses[0];
  return undefined; // no filters
};
await collection.query({ queryTexts, where: buildLogical('$and', clauses) });

Type guard

const isLogicalWhere = (w: unknown): w is { $and?: object[]; $or?: object[] } =>
  typeof w === 'object' && w !== null &&
  (Array.isArray((w as any).$and) || Array.isArray((w as any).$or));

Try / catch

try {
  await collection.query({ queryTexts, where });
} catch (e) {
  if ((e as Error).message.includes('$and or $or')) {
    // collapse a single-clause $and/$or to the bare clause and retry
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: where: { $and: [{ genre: 'sci-fi' }] } — single-element $and. where: { $and: [] }. Passing an object instead of an array: { $or: { a: 1 } } (one key).

Common situations: Programmatically building $and from a list of user facets where only one facet was selected; forgetting to fall back to a bare filter when the list has one element.

Related errors


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