chroma-core/chroma · error · ChromaValueError

Expected operand value to be a number for ${operator}, but g

Error message

Expected operand value to be a number for ${operator}, but got ${typeof operand}

What it means

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().

Source

Thrown at clients/new-js/packages/chromadb/src/utils.ts:583

      value.forEach((w: Where) => validateWhere(w));
      return;
    }

    if (typeof value === "object") {
      if (Object.keys(value).length != 1) {
        throw new ChromaValueError(
          `Expected operator expression to have one operator, but got ${value}`,
        );
      }

      const [operator, operand] = Object.entries(value)[0];

      if (
        ["$gt", "$gte", "$lt", "$lte"].includes(operator) &&
        typeof operand !== "number"
      ) {
        throw new ChromaValueError(
          `Expected operand value to be a number for ${operator}, but got ${typeof operand}`,
        );
      }

      if (["$in", "$nin"].includes(operator) && !Array.isArray(operand)) {
        throw new ChromaValueError(
          `Expected operand value to be an array for ${operator}, but got ${operand}`,
        );
      }

      if (
        ["$contains", "$not_contains"].includes(operator) &&
        !["string", "number", "boolean"].includes(typeof operand)
      ) {
        throw new ChromaValueError(
          `Expected operand value to be a string, number, or boolean for ${operator}, but got ${typeof operand}`,
        );
      }

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Convert operands: { $gte: Number(req.query.year) }.
  2. Store the filtered field as numeric metadata at ingestion time.
  3. For string matching use $eq/$contains instead of ordering operators.

Example fix

// before
where: { year: { $gte: req.query.year } } // '2020' string

// after
where: { year: { $gte: Number(req.query.year) } }
Defensive patterns

Strategy: validation

Validate before calling

const numeric = (v) => typeof v === 'number' ? v : Number(v);
await collection.query({ queryTexts, where: { year: { $gte: numeric(req.query.year) } } });

Type guard

const isNumericOperand = (v: unknown, op: string): v is number =>
  !['$gt', '$gte', '$lt', '$lte'].includes(op) || typeof v === 'number';

Try / catch

try {
  await collection.query({ queryTexts, where });
} catch (e) {
  if ((e as Error).message.includes('to be a number for')) {
    // coerce the operand with Number() and retry
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: where: { year: { $gte: '2020' } } — value taken from a URL/query param (always a string). { $lt: '1700000000' }. Attempting lexicographic string comparison: { name: { $gt: 'A' } }.

Common situations: HTTP query parameters arriving as strings; CSV/JSON ingestion leaving numbers as strings; trying to order strings with comparison operators (unsupported).

Related errors


AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16). Data as JSON: /api/errors/35be7c89fa249ffa. Report an issue: GitHub.