{"record":{"id":"54243934f6325b21","repo":"chroma-core/chroma","slug":"expected-operator-expression-to-have-one-operator","errorCode":null,"errorMessage":"Expected operator expression to have one operator, but got ${value}","messagePattern":"Expected operator expression to have one operator, but got (.+?)","errorType":"validation","errorClass":"ChromaValueError","httpStatus":null,"severity":"error","filePath":"clients/new-js/packages/chromadb/src/utils.ts","lineNumber":572,"sourceCode":"      throw new ChromaValueError(\n        `Expected 'where' value to be a string, number, boolean, or an operator expression, but got ${value}`,\n      );\n    }\n\n    if (key === \"$and\" || key === \"$or\") {\n      if (Object.keys(value).length <= 1) {\n        throw new ChromaValueError(\n          `Expected 'where' value for $and or $or to be a list of 'where' expressions, but got ${value}`,\n        );\n      }\n\n      value.forEach((w: Where) => validateWhere(w));\n      return;\n    }\n\n    if (typeof value === \"object\") {\n      if (Object.keys(value).length != 1) {\n        throw new ChromaValueError(\n          `Expected operator expression to have one operator, but got ${value}`,\n        );\n      }\n\n      const [operator, operand] = Object.entries(value)[0];\n\n      if (\n        [\"$gt\", \"$gte\", \"$lt\", \"$lte\"].includes(operator) &&\n        typeof operand !== \"number\"\n      ) {\n        throw new ChromaValueError(\n          `Expected operand value to be a number for ${operator}, but got ${typeof operand}`,\n        );\n      }\n\n      if ([\"$in\", \"$nin\"].includes(operator) && !Array.isArray(operand)) {\n        throw new ChromaValueError(\n          `Expected operand value to be an array for ${operator}, but got ${operand}`,","sourceCodeStart":554,"sourceCodeEnd":590,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/new-js/packages/chromadb/src/utils.ts#L554-L590","documentation":"When a field's value is an object, it is treated as an operator expression and must contain exactly one operator key. Combining two operators on one field — the classic range query { $gt: 1, $lt: 5 } — throws ChromaValueError because the expression has two keys. Range conditions must be split into separate clauses joined with $and.","triggerScenarios":"where: { year: { $gt: 2000, $lt: 2010 } }. Two operators in one brace block, e.g. { $contains: 'a', $eq: 'b' }. An empty operator object { field: {} } (0 keys).","commonSituations":"Porting range/date filters from Mongo or SQL BETWEEN syntax; assuming operator objects compose implicitly with AND inside one field expression.","solutions":["Split multi-operator fields: { $and: [{ year: { $gt: 2000 } }, { year: { $lt: 2010 } }] }.","Keep exactly one operator per brace block for a field.","For plain equality use the shorthand { field: value } instead of an operator object."],"exampleFix":"// before\nwhere: { year: { $gt: 2000, $lt: 2010 } }\n\n// after\nwhere: { $and: [{ year: { $gt: 2000 } }, { year: { $lt: 2010 } }] }","handlingStrategy":"validation","validationCode":"const range = (field, min, max) => ({\n  $and: [{ [field]: { $gt: min } }, { [field]: { $lt: max } }],\n});\nawait collection.query({ queryTexts, where: range('year', 2000, 2010) });","typeGuard":"const isSingleOperatorExpr = (v: object) => Object.keys(v).length === 1 && Object.keys(v)[0].startsWith('$');","tryCatchPattern":"try {\n  await collection.query({ queryTexts, where });\n} catch (e) {\n  if ((e as Error).message.includes('one operator')) {\n    // split the multi-operator expression into $and clauses and retry\n  } else {\n    throw e;\n  }\n}","preventionTips":["One operator per field expression — split ranges into $and clauses.","Provide a range() helper so callers never hand-write multi-operator objects.","For equality prefer the { field: value } shorthand."],"tags":["where-filter","validation","range-query"],"backgroundTag":"invalid-where-clause","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}