{"record":{"id":"d857738aa7887f4d","repo":"chroma-core/chroma","slug":"expected-wheredocument-to-have-exactly-one-opera","errorCode":null,"errorMessage":"Expected 'whereDocument' to have exactly one operator, but got ${whereDocument}","messagePattern":"Expected 'whereDocument' to have exactly one operator, but got (.+?)","errorType":"validation","errorClass":"ChromaValueError","httpStatus":null,"severity":"error","filePath":"clients/new-js/packages/chromadb/src/utils.ts","lineNumber":657,"sourceCode":"      }\n    }\n  });\n};\n\n/**\n * Validates a where document clause for document content filtering.\n * @param whereDocument - Where document clause to validate\n * @throws ChromaValueError if the clause is malformed\n */\nexport const validateWhereDocument = (whereDocument: WhereDocument) => {\n  if (typeof whereDocument !== \"object\") {\n    throw new ChromaValueError(\n      \"Expected 'whereDocument' to be a non-empty object\",\n    );\n  }\n\n  if (Object.keys(whereDocument).length != 1) {\n    throw new ChromaValueError(\n      `Expected 'whereDocument' to have exactly one operator, but got ${whereDocument}`,\n    );\n  }\n\n  const [operator, operand] = Object.entries(whereDocument)[0];\n  if (\n    ![\n      \"$contains\",\n      \"$not_contains\",\n      \"$matches\",\n      \"$not_matches\",\n      \"$regex\",\n      \"$not_regex\",\n      \"$and\",\n      \"$or\",\n    ].includes(operator)\n  ) {\n    throw new ChromaValueError(","sourceCodeStart":639,"sourceCodeEnd":675,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/new-js/packages/chromadb/src/utils.ts#L639-L675","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait col.query({ queryTexts: ['hi'], whereDocument: { $contains: 'foo', $not_contains: 'bar' } });\n\n// after\nawait col.query({ queryTexts: ['hi'], whereDocument: { $and: [{ $contains: 'foo' }, { $not_contains: 'bar' }] } });","handlingStrategy":"validation","validationCode":"const DOC_OPERATORS = ['$contains', '$not_contains', '$matches', '$not_matches', '$regex', '$not_regex', '$and', '$or'];\nfunction isValidWhereDocument(w: unknown): boolean {\n  if (typeof w !== 'object' || w === null) return false;\n  const keys = Object.keys(w);\n  if (keys.length !== 1 || !DOC_OPERATORS.includes(keys[0])) return false;\n  return true;\n}\n// before each call:\nif (whereDocument && !isValidWhereDocument(whereDocument)) throw new TypeError('bad whereDocument shape');","typeGuard":"import type { WhereDocument } from 'chromadb';\nconst DOC_OPS = ['$contains', '$not_contains', '$matches', '$not_matches', '$regex', '$not_regex', '$and', '$or'];\nexport const isWhereDocument = (w: unknown): w is WhereDocument =>\n  typeof w === 'object' && w !== null && Object.keys(w).length === 1 && DOC_OPS.includes(Object.keys(w)[0]);","tryCatchPattern":"try {\n  await col.query({ queryTexts, whereDocument });\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"whereDocument' to have exactly one operator\")) {\n    // rebuild as { $and: [clauseA, clauseB] } and retry\n  } else throw e;\n}","preventionTips":["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"],"tags":["javascript","typescript","validation","where-document","filters"],"backgroundTag":"invalid-query-filter","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}