chroma-core/chroma · error · TypeError

$and must be a non-empty array

Error message

$and must be a non-empty array

What it means

Thrown by parseWhereDict (where.ts:165) when the value of $and is not a non-empty array. The $and combinator must receive an array containing at least one clause; an empty array, a single clause object, a string, or null all throw this TypeError. Note $or has the identical rule in its own branch.

Source

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

  ["$in", (key, value) => new ComparisonWhere(key, "$in", value)],
  ["$nin", (key, value) => new ComparisonWhere(key, "$nin", value)],
  ["$contains", (key, value) => new ComparisonWhere(key, "$contains", value)],
  [
    "$not_contains",
    (key, value) => new ComparisonWhere(key, "$not_contains", value),
  ],
  ["$regex", (key, value) => new ComparisonWhere(key, "$regex", value)],
  ["$not_regex", (key, value) => new ComparisonWhere(key, "$not_regex", value)],
]);

const parseWhereDict = (data: Record<string, unknown>): WhereExpression => {
  if ("$and" in data) {
    if (Object.keys(data).length !== 1) {
      throw new Error("$and cannot be combined with other keys");
    }
    const rawConditions = data["$and"];
    if (!Array.isArray(rawConditions) || rawConditions.length === 0) {
      throw new TypeError("$and must be a non-empty array");
    }
    const conditions = rawConditions.map((item, index) => {
      const expr = WhereExpression.from(item as WhereInput);
      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) => AndWhere.combine(acc, condition),
        conditions[0],
      );
  }

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Use a non-empty array: { $and: [condA, condB] }
  2. For a single condition, drop $and entirely: just { field: { $eq: value } }
  3. Guard dynamic lists: clauses.length ? { $and: clauses } : clauses[0] ?? undefined

Example fix

// before
const where = { $and: conditions.filter(enabled) }; // may be []

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

Strategy: validation

Validate before calling

const clauses = rawClauses.filter(c => c != null);
if (clauses.length === 0) throw new Error('no where clauses to combine');
const where = clauses.length > 1 ? { $and: clauses } : clauses[0];

Type guard

const isNonEmptyClauseArray = (v: unknown): v is object[] =>
  Array.isArray(v) && v.length > 0;

Try / catch

try {
  const results = await collection.query({ where });
} catch (e) {
  if (e instanceof TypeError && e.message.includes('non-empty array')) {
    return collection.query({}); // drop the empty filter
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing { $and: [] } — e.g. the filter list was empty at runtime after all conditions were filtered out; { $and: { a: 1 } } — wrapping a single clause in $and without arraying it; { $and: null } or { $and: 'x' } from malformed config.

Common situations: Dynamically built filter lists where zero conditions matched (empty feature list, all optional filters unset); refactoring a single-condition filter into $and form without converting the value to an array; JSON configs where $and was hand-written as an object.

Related errors


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