{"record":{"id":"23db6991a0cec7bc","repo":"chroma-core/chroma","slug":"expected-wheredocument-operand-for-operator-t","errorCode":null,"errorMessage":"Expected 'whereDocument' operand for ${operator} to be a list with at least two 'whereDocument' expressions","messagePattern":"Expected 'whereDocument' operand for (.+?) to be a list with at least two 'whereDocument' expressions","errorType":"validation","errorClass":"ChromaValueError","httpStatus":null,"severity":"error","filePath":"clients/new-js/packages/chromadb/src/utils.ts","lineNumber":688,"sourceCode":"      \"$not_regex\",\n      \"$and\",\n      \"$or\",\n    ].includes(operator)\n  ) {\n    throw new ChromaValueError(\n      `Expected 'whereDocument' operator to be one of $contains, $not_contains, $matches, $not_matches, $regex, $not_regex, $and, or $or, but got ${operator}`,\n    );\n  }\n\n  if (operator === \"$and\" || operator === \"$or\") {\n    if (!Array.isArray(operand)) {\n      throw new ChromaValueError(\n        `Expected operand for ${operator} to be a list of 'whereDocument' expressions, but got ${operand}`,\n      );\n    }\n\n    if (operand.length <= 1) {\n      throw new ChromaValueError(\n        `Expected 'whereDocument' operand for ${operator} to be a list with at least two 'whereDocument' expressions`,\n      );\n    }\n\n    operand.forEach((item) => validateWhereDocument(item));\n  }\n\n  if (\n    (operand === \"$contains\" ||\n      operand === \"$not_contains\" ||\n      operand === \"$regex\" ||\n      operand === \"$not_regex\") &&\n    (typeof (operator as any) !== \"string\" || operator.length === 0)\n  ) {\n    throw new ChromaValueError(\n      `Expected operand for ${operator} to be a non empty string, but got ${operand}`,\n    );\n  }","sourceCodeStart":670,"sourceCodeEnd":706,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/new-js/packages/chromadb/src/utils.ts#L670-L706","documentation":"$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.","triggerScenarios":"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.","commonSituations":"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.","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]"],"exampleFix":"// before\nconst wd = { $and: predicates }; // predicates may have 0 or 1 items\n\n// after\nconst wd = predicates.length >= 2 ? { $and: predicates } : predicates[0];","handlingStrategy":"validation","validationCode":"function buildWhereDocument(predicates: Record<string, string>[]): Record<string, unknown> | undefined {\n  if (predicates.length === 0) return undefined;          // omit filter entirely\n  if (predicates.length === 1) return predicates[0];       // single clause, no wrapper\n  return { $and: predicates };                             // two or more\n}","typeGuard":"const isValidCompound = (w: { $and?: unknown[] } | { $or?: unknown[] }) => {\n  const list = (w as { $and?: unknown[] }).$and ?? (w as { $or?: unknown[] }).$or;\n  return list === undefined || (Array.isArray(list) && list.length >= 2);\n};","tryCatchPattern":"try {\n  await col.get({ whereDocument: { $and: predicates } });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('at least two')) {\n    const collapsed = predicates.length === 1 ? predicates[0] : undefined;\n    if (collapsed) await col.get({ whereDocument: collapsed });\n  } else throw e;\n}","preventionTips":["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"],"tags":["javascript","typescript","validation","where-document","logical-operators"],"backgroundTag":"invalid-query-filter","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}