chroma-core/chroma · error · ChromaValueError
Expected operand value to be a string, number, boolean, or a
Error message
Expected operand value to be a string, number, boolean, or a list of those types
What it means
As the final gate on operator operands, validateWhere requires a scalar (string/number/boolean) or an array; objects, null, undefined, and functions throw ChromaValueError. Notably this rejects { $eq: null } and { $ne: null } — Chroma where filters cannot compare against null. Nested objects as operands are also invalid.
Source
Thrown at clients/new-js/packages/chromadb/src/utils.ts:626
"$lte",
"$ne",
"$eq",
"$in",
"$nin",
"$contains",
"$not_contains",
].includes(operator)
) {
throw new ChromaValueError(
`Expected operator to be one of $gt, $gte, $lt, $lte, $ne, $eq, $in, $nin, $contains, $not_contains, but got ${operator}`,
);
}
if (
!["string", "number", "boolean"].includes(typeof operand) &&
!Array.isArray(operand)
) {
throw new ChromaValueError(
"Expected operand value to be a string, number, boolean, or a list of those types",
);
}
if (
Array.isArray(operand) &&
(operand.length === 0 ||
!operand.every((item) => typeof item === typeof operand[0]))
) {
throw new ChromaValueError(
"Expected 'where' operand value to be a non-empty list and all values to be of the same type",
);
}
}
});
};
/**View on GitHub (pinned to aecdd12c8a)
Solutions
- Chroma cannot filter on null/missing metadata — restructure so absence is a stored value, e.g. a boolean flag or the string 'none'.
- Unwrap nested objects to the scalar before use.
- Filter out undefined operands when building the where clause.
Example fix
// before
where: { finished_at: { $eq: null } }
// after (store a boolean flag at ingestion)
where: { is_finished: false } Defensive patterns
Strategy: type-guard
Validate before calling
const isOperand = (v) =>
['string', 'number', 'boolean'].includes(typeof v) || Array.isArray(v);
const operandsOk = Object.values(where).every(v =>
typeof v !== 'object' || v === null ? isOperand(v) : Object.values(v).every(isOperand)
);
if (!operandsOk) throw new Error('Where operands must be scalars or arrays, not null/objects'); Type guard
const isScalarOrList = (v: unknown): v is string | number | boolean | unknown[] => ['string', 'number', 'boolean'].includes(typeof v) || Array.isArray(v);
Try / catch
try {
await collection.query({ queryTexts, where });
} catch (e) {
if ((e as Error).message.includes('string, number, boolean, or a list')) {
// replace null operands with a stored sentinel value and retry
} else {
throw e;
}
} Prevention
- Never filter with $eq: null / $ne: null — Chroma does not support null comparisons.
- Model optional fields as stored sentinels (false, 'none') at ingestion time.
- Keep operands flat scalars or scalar lists.
When it happens
Trigger: where: { finished_at: { $eq: null } } — attempting to match missing metadata. { field: { $ne: undefined } }. { $gt: { value: 10 } } (wrapped object).
Common situations: Optional fields modeled as null in metadata and filtered with $eq: null; building operands from nested config objects; undefined leaking through from JS option objects.
Related errors
- Expected each document to be a string, but got ${typeof docu
- Expected where to be a non-empty object
- Expected 'where' to have exactly one operator, but got ${Obj
- Expected 'where' value to be a string, number, boolean, or a
- Expected 'where' value for $and or $or to be a list of 'wher
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/72d33222fb4e0180.
Report an issue: GitHub.