chroma-core/chroma · error · ChromaValueError
Expected 'whereDocument' to have exactly one operator, but g
Error message
Expected 'whereDocument' to have exactly one operator, but got ${whereDocument} What it means
The JS/TS Chroma client validates every whereDocument filter locally before any request is sent (validateWhereDocument in utils.ts, called from validateGetRequest, prepareQuery and validateDelete in collection.ts). A whereDocument clause must be an object with exactly one top-level operator key, e.g. { $contains: 'hello' } or { $and: [...] }, matching the WhereDocument union in types.ts. When Object.keys(whereDocument).length is 0 or greater than 1, this ChromaValueError is thrown client-side and the request never leaves the process.
Source
Thrown at clients/new-js/packages/chromadb/src/utils.ts:657
}
}
});
};
/**
* Validates a where document clause for document content filtering.
* @param whereDocument - Where document clause to validate
* @throws ChromaValueError if the clause is malformed
*/
export const validateWhereDocument = (whereDocument: WhereDocument) => {
if (typeof whereDocument !== "object") {
throw new ChromaValueError(
"Expected 'whereDocument' to be a non-empty object",
);
}
if (Object.keys(whereDocument).length != 1) {
throw new ChromaValueError(
`Expected 'whereDocument' to have exactly one operator, but got ${whereDocument}`,
);
}
const [operator, operand] = Object.entries(whereDocument)[0];
if (
![
"$contains",
"$not_contains",
"$matches",
"$not_matches",
"$regex",
"$not_regex",
"$and",
"$or",
].includes(operator)
) {
throw new ChromaValueError(View on GitHub (pinned to aecdd12c8a)
Solutions
- Keep exactly one operator per whereDocument object and combine clauses with { $and: [clause1, clause2] } or { $or: [...] }
- Omit the whereDocument parameter entirely (undefined) when no document filter is needed instead of passing {}
- Type the argument as the package's WhereDocument union so the compiler rejects multi-key object literals
Example fix
// before
await col.query({ queryTexts: ['hi'], whereDocument: { $contains: 'foo', $not_contains: 'bar' } });
// after
await col.query({ queryTexts: ['hi'], whereDocument: { $and: [{ $contains: 'foo' }, { $not_contains: 'bar' }] } }); Defensive patterns
Strategy: validation
Validate before calling
const DOC_OPERATORS = ['$contains', '$not_contains', '$matches', '$not_matches', '$regex', '$not_regex', '$and', '$or'];
function isValidWhereDocument(w: unknown): boolean {
if (typeof w !== 'object' || w === null) return false;
const keys = Object.keys(w);
if (keys.length !== 1 || !DOC_OPERATORS.includes(keys[0])) return false;
return true;
}
// before each call:
if (whereDocument && !isValidWhereDocument(whereDocument)) throw new TypeError('bad whereDocument shape'); Type guard
import type { WhereDocument } from 'chromadb';
const DOC_OPS = ['$contains', '$not_contains', '$matches', '$not_matches', '$regex', '$not_regex', '$and', '$or'];
export const isWhereDocument = (w: unknown): w is WhereDocument =>
typeof w === 'object' && w !== null && Object.keys(w).length === 1 && DOC_OPS.includes(Object.keys(w)[0]); Try / catch
try {
await col.query({ queryTexts, whereDocument });
} catch (e) {
if (e instanceof Error && e.message.includes("whereDocument' to have exactly one operator")) {
// rebuild as { $and: [clauseA, clauseB] } and retry
} else throw e;
} Prevention
- Model every clause as a single-key object and combine exclusively through $and/$or arrays
- Type filter parameters as WhereDocument instead of any/Record<string, unknown>
- Unit-test your filter builder so it never emits {} or sibling operators
When it happens
Trigger: collection.get({ whereDocument: {} }); collection.query({ queryTexts: [...], whereDocument: { $contains: 'a', $not_contains: 'b' } }) (two operator keys as siblings); collection.delete({ whereDocument: [] }) — an array is typeof 'object' with zero keys and lands here too.
Common situations: Dynamically composing filters with object spread so multiple operators end up as siblings; copying `where` syntax (which allows one key per metadata field) into whereDocument; passing an empty object as a placeholder when the filter is optional.
Related errors
- Expected 'whereDocument' operator to be one of $contains, $n
- Expected operand for ${operator} to be a list of 'whereDocum
- Expected 'whereDocument' operand for ${operator} to be a lis
- 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/d857738aa7887f4d.
Report an issue: GitHub.