{"record":{"id":"c0701fdbab775fa4","repo":"chroma-core/chroma","slug":"expected-wheredocument-to-be-a-non-empty-object","errorCode":null,"errorMessage":"Expected 'whereDocument' to be a non-empty object","messagePattern":"Expected 'whereDocument' to be a non-empty object","errorType":"validation","errorClass":"ChromaValueError","httpStatus":null,"severity":"error","filePath":"clients/new-js/packages/chromadb/src/utils.ts","lineNumber":651,"sourceCode":"        (operand.length === 0 ||\n          !operand.every((item) => typeof item === typeof operand[0]))\n      ) {\n        throw new ChromaValueError(\n          \"Expected 'where' operand value to be a non-empty list and all values to be of the same type\",\n        );\n      }\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\",","sourceCodeStart":633,"sourceCodeEnd":669,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/new-js/packages/chromadb/src/utils.ts#L633-L669","documentation":"validateWhereDocument is the whereDocument counterpart of the where type gate: a non-object value (string, number, etc.) throws ChromaValueError. whereDocument must be an object with exactly one operator (e.g. $contains) whose operand is a string. Passing raw search text directly is the classic mistake.","triggerScenarios":"collection.query({ whereDocument: 'machine learning' }) — raw string. whereDocument: 42. A JSON string that was never parsed.","commonSituations":"Assuming whereDocument takes search text directly; wiring a search box input straight into the option; confusing content filtering (whereDocument) with semantic search (queryTexts).","solutions":["Wrap text in an operator: whereDocument: { $contains: 'machine learning' }.","Parse JSON strings before passing them.","Use queryTexts or queryEmbeddings for semantic search — whereDocument is only for content filtering."],"exampleFix":"// before\nawait collection.query({ queryTexts: ['ai'], whereDocument: 'machine learning' });\n\n// after\nawait collection.query({ queryTexts: ['ai'], whereDocument: { $contains: 'machine learning' } });","handlingStrategy":"type-guard","validationCode":"if (whereDocument !== undefined && (typeof whereDocument !== 'object' || whereDocument === null)) {\n  throw new TypeError('whereDocument must be like { $contains: \\'text\\' }');\n}\nawait collection.query({ queryTexts, whereDocument });","typeGuard":"const isWhereDocument = (w: unknown): w is { $contains?: string; $not_contains?: string } =>\n  typeof w === 'object' && w !== null && !Array.isArray(w);","tryCatchPattern":"try {\n  await collection.query({ queryTexts, whereDocument });\n} catch (e) {\n  if ((e as Error).message.includes('whereDocument')) {\n    // wrap the raw text as { $contains: text } and retry\n  } else {\n    throw e;\n  }\n}","preventionTips":["Always wrap document filter text in { $contains: ... }.","Use queryTexts for semantic search; whereDocument only narrows by content.","Parse serialized filter objects before passing them to query()."],"tags":["where-document","validation","query"],"backgroundTag":"invalid-where-clause","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}