chroma-core/chroma · error · ChromaValueError

Expected operand value to be a string, number, boolean, or a

Error message

Expected operand value to be a string, number, boolean, or a list of those types

What it means

As the final gate on operator operands, validateWhere requires a scalar (string/number/boolean) or an array; objects, null, undefined, and functions throw ChromaValueError. Notably this rejects { $eq: null } and { $ne: null } — Chroma where filters cannot compare against null. Nested objects as operands are also invalid.

Source

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

          "$lte",
          "$ne",
          "$eq",
          "$in",
          "$nin",
          "$contains",
          "$not_contains",
        ].includes(operator)
      ) {
        throw new ChromaValueError(
          `Expected operator to be one of $gt, $gte, $lt, $lte, $ne, $eq, $in, $nin, $contains, $not_contains, but got ${operator}`,
        );
      }

      if (
        !["string", "number", "boolean"].includes(typeof operand) &&
        !Array.isArray(operand)
      ) {
        throw new ChromaValueError(
          "Expected operand value to be a string, number, boolean, or a list of those types",
        );
      }

      if (
        Array.isArray(operand) &&
        (operand.length === 0 ||
          !operand.every((item) => typeof item === typeof operand[0]))
      ) {
        throw new ChromaValueError(
          "Expected 'where' operand value to be a non-empty list and all values to be of the same type",
        );
      }
    }
  });
};

/**

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Chroma cannot filter on null/missing metadata — restructure so absence is a stored value, e.g. a boolean flag or the string 'none'.
  2. Unwrap nested objects to the scalar before use.
  3. Filter out undefined operands when building the where clause.

Example fix

// before
where: { finished_at: { $eq: null } }

// after (store a boolean flag at ingestion)
where: { is_finished: false }
Defensive patterns

Strategy: type-guard

Validate before calling

const isOperand = (v) =>
  ['string', 'number', 'boolean'].includes(typeof v) || Array.isArray(v);
const operandsOk = Object.values(where).every(v =>
  typeof v !== 'object' || v === null ? isOperand(v) : Object.values(v).every(isOperand)
);
if (!operandsOk) throw new Error('Where operands must be scalars or arrays, not null/objects');

Type guard

const isScalarOrList = (v: unknown): v is string | number | boolean | unknown[] =>
  ['string', 'number', 'boolean'].includes(typeof v) || Array.isArray(v);

Try / catch

try {
  await collection.query({ queryTexts, where });
} catch (e) {
  if ((e as Error).message.includes('string, number, boolean, or a list')) {
    // replace null operands with a stored sentinel value and retry
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: where: { finished_at: { $eq: null } } — attempting to match missing metadata. { field: { $ne: undefined } }. { $gt: { value: 10 } } (wrapped object).

Common situations: Optional fields modeled as null in metadata and filtered with $eq: null; building operands from nested config objects; undefined leaking through from JS option objects.

Related errors


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