chroma-core/chroma · error · TypeError

Where input must be a WhereExpression or plain object

Error message

Where input must be a WhereExpression or plain object

What it means

Thrown by WhereExpression.from (where.ts:35) when a where filter is neither a WhereExpression instance, null/undefined, nor a plain object (Object.prototype or null prototype, not an array). The dict form is then parsed by parseWhereDict. This is a strict structural gate: arrays of clauses, primitives, Maps, and class instances are all rejected client-side before any request.

Source

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

  public or(other: WhereInput): WhereExpression {
    const target = WhereExpression.from(other);
    if (!target) {
      return this as unknown as WhereExpression;
    }
    return OrWhere.combine(this as unknown as WhereExpression, target);
  }
}

export abstract class WhereExpression extends WhereExpressionBase {
  public static from(input: WhereInput): WhereExpression | undefined {
    if (input instanceof WhereExpression) {
      return input;
    }
    if (input === null || input === undefined) {
      return undefined;
    }
    if (!isPlainObject(input)) {
      throw new TypeError(
        "Where input must be a WhereExpression or plain object",
      );
    }
    return parseWhereDict(input);
  }
}

class AndWhere extends WhereExpression {
  constructor(private readonly conditions: WhereExpression[]) {
    super();
  }

  public toJSON(): WhereJSON {
    return { $and: this.conditions.map((condition) => condition.toJSON()) };
  }

  public get operands(): WhereExpression[] {
    return this.conditions.slice();

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Pass a single plain-object dict: where({ genre: { $eq: 'sci-fi' } })
  2. Combine multiple clauses with $and/$or keys or build with WhereExpression .and()/.or()
  3. For class-instance sources, serialize first (JSON.parse(JSON.stringify(obj))) or construct the dict explicitly

Example fix

// before
const results = await collection.query({ where: [condA, condB] }); // array throws

// after
const results = await collection.query({
  where: { $and: [condA, condB] },
});
Defensive patterns

Strategy: type-guard

Validate before calling

const isPlainObjectLike = (v: unknown): v is Record<string, unknown> => {
  if (typeof v !== 'object' || v === null || Array.isArray(v)) return false;
  const p = Object.getPrototypeOf(v);
  return p === Object.prototype || p === null;
};
if (!isPlainObjectLike(where)) throw new Error('where must be a dict');

Type guard

const isWhereDict = (v: unknown): v is Record<string, unknown> =>
  isPlainObjectLike(v); // WhereExpression instances also accepted by WhereExpression.from

Try / catch

try {
  const results = await collection.query({ where });
} catch (e) {
  if (e instanceof TypeError && e.message.includes('Where input')) {
    throw new Error(`Invalid where filter shape: ${JSON.stringify(where)}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing where: [{ genre: {$eq: 'a'} }, { year: {$gt: 2020} }] — an array of clauses instead of one dict (nest them under $and instead); where: 'genre' or where: 42 — a primitive; where: someClassInstance — an object whose prototype is not Object.prototype; calling expr.and(mapObject) with a Map.

Common situations: Translating Mongo-style filters where an array of conditions is idiomatic; filters built by class hierarchies or from libraries that return wrapped objects; JSON filters are safe (JSON.parse yields plain objects) but Object.create(customProto) is not; wrapping the filter in an extra layer like { where: {...} } by mistake.

Related errors


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