chroma-core/chroma · error · ChromaValueError
Expected 'whereDocument' operand for ${operator} to be a lis
Error message
Expected 'whereDocument' operand for ${operator} to be a list with at least two 'whereDocument' expressions What it means
$and and $or require a list with at least two whereDocument expressions (utils.ts:687-691); a conjunction or disjunction of zero or one clause is rejected by design. After the length check, each element is re-validated recursively, so nested clauses get the same one-operator rule.
Source
Thrown at clients/new-js/packages/chromadb/src/utils.ts:688
"$not_regex",
"$and",
"$or",
].includes(operator)
) {
throw new ChromaValueError(
`Expected 'whereDocument' operator to be one of $contains, $not_contains, $matches, $not_matches, $regex, $not_regex, $and, or $or, but got ${operator}`,
);
}
if (operator === "$and" || operator === "$or") {
if (!Array.isArray(operand)) {
throw new ChromaValueError(
`Expected operand for ${operator} to be a list of 'whereDocument' expressions, but got ${operand}`,
);
}
if (operand.length <= 1) {
throw new ChromaValueError(
`Expected 'whereDocument' operand for ${operator} to be a list with at least two 'whereDocument' expressions`,
);
}
operand.forEach((item) => validateWhereDocument(item));
}
if (
(operand === "$contains" ||
operand === "$not_contains" ||
operand === "$regex" ||
operand === "$not_regex") &&
(typeof (operator as any) !== "string" || operator.length === 0)
) {
throw new ChromaValueError(
`Expected operand for ${operator} to be a non empty string, but got ${operand}`,
);
}View on GitHub (pinned to aecdd12c8a)
Solutions
- If only one predicate remains, pass it directly instead of wrapping it in $and/$or
- If zero predicates remain, omit the whereDocument parameter entirely
- Guard dynamic builders: predicates.length >= 2 ? { $and: predicates } : predicates[0]
Example fix
// before
const wd = { $and: predicates }; // predicates may have 0 or 1 items
// after
const wd = predicates.length >= 2 ? { $and: predicates } : predicates[0]; Defensive patterns
Strategy: validation
Validate before calling
function buildWhereDocument(predicates: Record<string, string>[]): Record<string, unknown> | undefined {
if (predicates.length === 0) return undefined; // omit filter entirely
if (predicates.length === 1) return predicates[0]; // single clause, no wrapper
return { $and: predicates }; // two or more
} Type guard
const isValidCompound = (w: { $and?: unknown[] } | { $or?: unknown[] }) => {
const list = (w as { $and?: unknown[] }).$and ?? (w as { $or?: unknown[] }).$or;
return list === undefined || (Array.isArray(list) && list.length >= 2);
}; Try / catch
try {
await col.get({ whereDocument: { $and: predicates } });
} catch (e) {
if (e instanceof Error && e.message.includes('at least two')) {
const collapsed = predicates.length === 1 ? predicates[0] : undefined;
if (collapsed) await col.get({ whereDocument: collapsed });
} else throw e;
} Prevention
- Never emit $and/$or from a builder without checking predicates.length >= 2
- Treat zero active filters as 'omit whereDocument', not as an empty $and
- Property-test filter builders over 0/1/2+ predicate counts
When it happens
Trigger: whereDocument: { $and: [] }; { $or: [{ $contains: 'x' }] } (single element); typically from collection.get()/query()/delete() where the $and array was built dynamically and ended up with 0 or 1 predicates.
Common situations: Programmatically composing $and from a runtime predicate list (e.g. search filters where the user filled in none or only one field) — the list length drops below two.
Related errors
- Expected operand for ${operator} to be a list of 'whereDocum
- Expected 'whereDocument' to have exactly one operator, but g
- Expected 'whereDocument' operator to be one of $contains, $n
- Expected operand for ${operator} to be a non empty string, b
- Expected 'include' to be a non-empty array
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/23db6991a0cec7bc.
Report an issue: GitHub.