hcengineering/platform · error · Error

$in predicate requires array

Error message

$in predicate requires array

What it means

The $in query predicate must be given an array of candidate values. The factory in predicate.ts validates its argument eagerly and throws this error when the value passed after $in is not an Array, because membership testing (o.some(p => value === p)) only makes sense over a list. This matches MongoDB semantics where $in takes an array.

Source

Thrown at foundations/core/packages/core/src/predicate.ts:42

type PredicateFactory = (pred: any, propertyKey: string) => Predicate

type ExecPredicate = (value: any) => boolean

function execPredicate (docs: Doc[], propertyKey: string, pred: ExecPredicate): Doc[] {
  const result: Doc[] = []
  for (const doc of docs) {
    const value = getObjectValue(propertyKey, doc)
    if (pred(value)) {
      result.push(doc)
    }
  }
  return result
}

const predicates: Record<string, PredicateFactory> = {
  $in: (o, propertyKey) => {
    if (!Array.isArray(o)) {
      throw new Error('$in predicate requires array')
    }
    return (docs) =>
      execPredicate(docs, propertyKey, (value) => {
        if (Array.isArray(value)) {
          return o.some((p) => value.includes(p))
        } else {
          // eslint-disable-next-line eqeqeq
          return o.some((p) => p == value)
        }
      })
  },
  $all: (o, propertyKey) => {
    if (!Array.isArray(o)) {
      throw new Error('$all predicate requires array')
    }
    return (docs) =>
      execPredicate(docs, propertyKey, (value: any[]) => {
        for (const val of o) {

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Wrap the value in an array: { $in: [value] } instead of { $in: value }.
  2. Guard dynamic values with Array.isArray before building the query and normalize single values to [value].
  3. Check upstream code that produces the list (API call, map/filter) to ensure it returns an array, not undefined.

Example fix

// before
const q = { status: { $in: statusesFromApi } }
// after
const list = Array.isArray(statusesFromApi) ? statusesFromApi : statusesFromApi != null ? [statusesFromApi] : []
const q = { status: { $in: list } }
Defensive patterns

Strategy: type-guard

Validate before calling

function asArray<T>(v: T | T[] | undefined | null): T[] {
  return Array.isArray(v) ? v : v != null ? [v] : []
}
const q = { status: { $in: asArray(statuses) } }

Type guard

function isArrayOrThrow<T>(v: unknown): asserts v is T[] {
  if (!Array.isArray(v)) throw new TypeError('$in expects an array of values')
}

Try / catch

try {
  return await find(clazz, query)
} catch (e) {
  if (e instanceof Error && e.message.includes('predicate requires array')) {
    return await find(clazz, normalizeQueryArrays(query))
  }
  throw e
}

Prevention

When it happens

Trigger: Building a query like { status: { $in: 'active' } } (a bare string), { _id: { $in: someId } } where someId failed to come back as an array, or spreading a possibly-undefined variable: { $in: maybeIds } when maybeIds is undefined or a single object.

Common situations: Passing a single value instead of wrapping it in an array; a result of Array.prototype.filter/map or an API response expected to be an array but actually undefined/null; deserialized query params (JSON config, URL query) arriving as a scalar.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/ba045523b9ab5d3a. Report an issue: GitHub.