chroma-core/chroma · error · Error
Where dictionary must contain exactly one field
Error message
Where dictionary must contain exactly one field
What it means
Chroma's where grammar allows exactly one field (or one logical operator) per dictionary level: each dict is either { $and/$or: [...] } or { field: value }. A dictionary with zero or multiple field keys — like { category: 'news', status: 'active' } or {} — fails parsing with this error before any request is sent. Multiple conditions must be combined with $and/$or or the fluent .and() builder.
Source
Thrown at clients/new-js/packages/chromadb/src/execution/expression/where.ts:213
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) => OrWhere.combine(acc, condition),
conditions[0],
);
}
const entries = Object.entries(data);
if (entries.length !== 1) {
throw new Error("Where dictionary must contain exactly one field");
}
const [field, value] = entries[0];
if (!isPlainObject(value)) {
return new ComparisonWhere(field, "$eq", value);
}
const operatorEntries = Object.entries(value);
if (operatorEntries.length !== 1) {
throw new Error(
`Operator dictionary for field "${field}" must contain exactly one operator`,
);
}
const [operator, operand] = operatorEntries[0];
const factory = comparisonOperatorMap.get(operator);
if (!factory) {
throw new Error(`Unsupported where operator: ${operator}`);View on GitHub (pinned to aecdd12c8a)
Solutions
- Wrap each condition in its own dict and combine: { $and: [{ category: 'news' }, { status: 'active' }] }.
- Or use the builder: WhereExpression.from({ category: 'news' }).and({ status: 'active' }).
- Replace object-spread merging of filters with an array of clauses reduced into { $and: clauses }.
- Drop empty dicts ({}): skip filters that resolved to nothing instead of merging them in.
Example fix
// before
where: { ...categoryFilter, ...statusFilter } // { category: 'news', status: 'active' }
// after
where: { $and: [categoryFilter, statusFilter] } // one field per dict Defensive patterns
Strategy: validation
Validate before calling
function toChromaWhere(filters: Record<string, unknown>[]): Record<string, unknown> | undefined {
const clauses = filters.filter((f) => Object.keys(f).length === 1);
if (clauses.length !== filters.length) {
throw new Error('Each filter dict must contain exactly one field');
}
if (clauses.length === 0) return undefined;
if (clauses.length === 1) return clauses[0];
return { $and: clauses };
} Type guard
function isSingleFieldDict(where: unknown): boolean {
return (
typeof where === 'object' &&
where !== null &&
!Array.isArray(where) &&
Object.keys(where).length === 1
);
} Try / catch
try {
await collection.query({ where });
} catch (e) {
if (e instanceof Error && e.message === 'Where dictionary must contain exactly one field') {
// split where into one-field dicts and rewrap as { $and: [...] }
} else {
throw e;
}
} Prevention
- Never merge Chroma where dicts with object spread; collect them into an $and/$or array instead.
- Keep one shared helper that reduces a list of single-field filters into valid Chroma shape.
- Remember the grammar: one key per level; nesting replaces multi-field dicts.
When it happens
Trigger: where: { category: 'news', status: 'active' }; merging two valid one-field dicts with object spread ({ ...filterA, ...filterB }); where: {} (zero entries also fails the exactly-one check); pasting a MongoDB-style multi-field query verbatim.
Common situations: Combining independent filters with object spread — a JS idiom that works for Mongo-style APIs but violates Chroma's one-key-per-level grammar; migrating from the Python client or Mongo where multi-field dicts are legal; building filters from generic key/value config maps.
Related errors
- $or must be a non-empty array
- Operator dictionary for field "${field}" must contain exactl
- Unsupported where operator: ${operator}
- K.DOCUMENT.contains requires a string value
- K.DOCUMENT.notContains requires a string value
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/d3d4fb8e9ec6f3d0.
Report an issue: GitHub.