{"record":{"id":"35be7c89fa249ffa","repo":"chroma-core/chroma","slug":"expected-operand-value-to-be-a-number-for-operat","errorCode":null,"errorMessage":"Expected operand value to be a number for ${operator}, but got ${typeof operand}","messagePattern":"Expected operand value to be a number for (.+?), but got (.+?)","errorType":"validation","errorClass":"ChromaValueError","httpStatus":null,"severity":"error","filePath":"clients/new-js/packages/chromadb/src/utils.ts","lineNumber":583,"sourceCode":"\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}`,\n        );\n      }\n\n      if (\n        [\"$contains\", \"$not_contains\"].includes(operator) &&\n        ![\"string\", \"number\", \"boolean\"].includes(typeof operand)\n      ) {\n        throw new ChromaValueError(\n          `Expected operand value to be a string, number, or boolean for ${operator}, but got ${typeof operand}`,\n        );\n      }","sourceCodeStart":565,"sourceCodeEnd":601,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/new-js/packages/chromadb/src/utils.ts#L565-L601","documentation":"Comparison operators $gt, $gte, $lt, $lte accept only numeric operands; validateWhere throws when the operand's typeof is not number. String digits like { $gte: '10' } are rejected even though they look numeric, because Chroma compares typed metadata. Use a real number or convert with Number().","triggerScenarios":"where: { year: { $gte: '2020' } } — value taken from a URL/query param (always a string). { $lt: '1700000000' }. Attempting lexicographic string comparison: { name: { $gt: 'A' } }.","commonSituations":"HTTP query parameters arriving as strings; CSV/JSON ingestion leaving numbers as strings; trying to order strings with comparison operators (unsupported).","solutions":["Convert operands: { $gte: Number(req.query.year) }.","Store the filtered field as numeric metadata at ingestion time.","For string matching use $eq/$contains instead of ordering operators."],"exampleFix":"// before\nwhere: { year: { $gte: req.query.year } } // '2020' string\n\n// after\nwhere: { year: { $gte: Number(req.query.year) } }","handlingStrategy":"validation","validationCode":"const numeric = (v) => typeof v === 'number' ? v : Number(v);\nawait collection.query({ queryTexts, where: { year: { $gte: numeric(req.query.year) } } });","typeGuard":"const isNumericOperand = (v: unknown, op: string): v is number =>\n  !['$gt', '$gte', '$lt', '$lte'].includes(op) || typeof v === 'number';","tryCatchPattern":"try {\n  await collection.query({ queryTexts, where });\n} catch (e) {\n  if ((e as Error).message.includes('to be a number for')) {\n    // coerce the operand with Number() and retry\n  } else {\n    throw e;\n  }\n}","preventionTips":["Convert query-param strings to numbers before building filters.","Store numeric-filter fields as numbers in metadata at ingestion.","Do not use $gt/$lt on string fields — Chroma only supports numeric comparisons."],"tags":["where-filter","type-error","validation"],"backgroundTag":"invalid-where-clause","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}