{"record":{"id":"65206e4c3143193f","repo":"chroma-core/chroma","slug":"operator-dictionary-for-field-field-must-cont","errorCode":null,"errorMessage":"Operator dictionary for field \"${field}\" must contain exactly one operator","messagePattern":"Operator dictionary for field \"(.+?)\" must contain exactly one operator","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"clients/new-js/packages/chromadb/src/execution/expression/where.ts","lineNumber":223,"sourceCode":"      .reduce(\n        (acc, condition) => OrWhere.combine(acc, condition),\n        conditions[0],\n      );\n  }\n\n  const entries = Object.entries(data);\n  if (entries.length !== 1) {\n    throw new Error(\"Where dictionary must contain exactly one field\");\n  }\n\n  const [field, value] = entries[0];\n  if (!isPlainObject(value)) {\n    return new ComparisonWhere(field, \"$eq\", value);\n  }\n\n  const operatorEntries = Object.entries(value);\n  if (operatorEntries.length !== 1) {\n    throw new Error(\n      `Operator dictionary for field \"${field}\" must contain exactly one operator`,\n    );\n  }\n\n  const [operator, operand] = operatorEntries[0];\n  const factory = comparisonOperatorMap.get(operator);\n  if (!factory) {\n    throw new Error(`Unsupported where operator: ${operator}`);\n  }\n\n  return factory(field, operand);\n};\n\nexport const createComparisonWhere = (\n  key: string,\n  operator: string,\n  value: unknown,\n): WhereExpression => new ComparisonWhere(key, operator, value);","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/new-js/packages/chromadb/src/execution/expression/where.ts#L205-L241","documentation":"When a field's value in a where dict is a plain object, the parser treats it as an operator dictionary that must contain exactly one operator key. Two operators on the same field in one dict — typically a range like { price: { $gte: 10, $lte: 100 } } — are rejected, as is an empty operator object { field: {} }. Split the operators into separate clauses joined by $and.","triggerScenarios":"Range filters: where: { price: { $gte: 10, $lte: 100 } }; time windows: { ts: { $gt: start, $lt: end } }; two equality-ish operators in one dict: { status: { $eq: 'a', $ne: 'b' } }; { field: {} } built when an operator/value lookup came back empty.","commonSituations":"Porting MongoDB range queries that allow multiple operators per field; date/time-window filters; price slider UIs; filter builders that merge operator dicts with spread.","solutions":["Split the range: { $and: [{ price: { $gte: 10 } }, { price: { $lte: 100 } }] }.","Or use the builder: WhereExpression.from({ price: { $gte: 10 } }).and({ price: { $lte: 100 } }).","If the two operators were meant as alternatives, join them with $or instead.","Guard against empty operator dicts when generating filters dynamically."],"exampleFix":"// before\nwhere: { price: { $gte: 10, $lte: 100 } }\n\n// after\nwhere: { $and: [{ price: { $gte: 10 } }, { price: { $lte: 100 } }] }","handlingStrategy":"validation","validationCode":"function rangeFilter(field: string, min: number, max: number) {\n  return { $and: [{ [field]: { $gte: min } }, { [field]: { $lte: max } }] };\n}","typeGuard":"function isSingleOperatorDict(value: unknown): boolean {\n  if (typeof value !== 'object' || value === null || Array.isArray(value)) return true;\n  return Object.keys(value).length === 1;\n}","tryCatchPattern":"try {\n  await collection.query({ where });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('must contain exactly one operator')) {\n    // split the field's operator dict into $and-joined single-operator clauses\n  } else {\n    throw e;\n  }\n}","preventionTips":["Use one operator per field dict; join ranges with $and.","Write range/equality/set filter helpers once instead of hand-building multi-operator dicts.","Add table-driven unit tests for your filter builder covering range, set, and substring cases."],"tags":["where-filter","range-query","query-validation","chroma"],"backgroundTag":"invalid-where-clause","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}