hcengineering/platform · error

$size predicate requires array

Error message

$size predicate requires array

What it means

$size matches documents whose property is an array of a particular length. Unlike $in/$all/$nin, this check happens at evaluation time against the document's field value: when a matched document's property is not an array (or is a non-null scalar/object), predicate.ts throws this error while running the query. It indicates the queried field does not consistently hold arrays.

Solutions

  1. Fix the data so the field is always an array (migrate single values to [value]).
  2. Ensure write paths always assign arrays, even for one element.
  3. Pre-filter or $exists-guard the field, or use a different predicate ($exists, $ne) for non-array fields.
  4. Wrap query execution in try/catch if heterogeneous legacy data must be tolerated.

Example fix

// before (writer)
await update(doc, { $set: { attachments: file } })
// after
await update(doc, { $set: { attachments: [file] } })
Defensive patterns

Strategy: validation

Validate before calling

// Before querying with $size, ensure the field holds arrays everywhere
const bad = docs.find((d) => d.attachments != null && !Array.isArray(d.attachments))
if (bad != null) throw new Error(`doc ${bad._id} has non-array attachments; migrate before using $size`)

Type guard

function hasArrayField<K extends string>(doc: Record<string, unknown>, key: K): doc is Record<K, unknown[]> {
  return Array.isArray(doc[key])
}

Try / catch

try {
  return await find(clazz, { attachments: { $size: 3 } })
} catch (e) {
  if (e instanceof Error && e.message.startsWith('$size predicate requires array')) {
    // fall back: filter in code over non-array-tolerant predicate
    const all = await find(clazz, { attachments: { $exists: true } })
    return all.filter((d) => Array.isArray(d.attachments) && d.attachments.length === 3)
  }
  throw e
}

Prevention

When it happens

Trigger: Querying { attachments: { $size: 3 } } when some documents store attachments as undefined-is-guarded (returns false) but others store a scalar or a plain object (e.g. a single attachment object instead of an array) — evaluation then throws on those docs.

Common situations: Schema drift where older documents or imports stored the field as a single value; data written by another tool/version with a different shape; optional fields populated inconsistently across documents.

Related errors


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

Appendix: source

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

  },
  $lte: (o, propertyKey) => {
    return (docs) => execPredicate(docs, propertyKey, (value) => value <= o)
  },
  $exists: (o, propertyKey) => {
    return (docs) => execPredicate(docs, propertyKey, (value) => (value !== undefined) === o)
  },
  $ne: (o, propertyKey) => {
    // eslint-disable-next-line eqeqeq
    return (docs) => execPredicate(docs, propertyKey, (value) => (o != null ? !deepEqual(o, value) : value != null))
  },
  $size: (o, propertyKey) => {
    return (docs) =>
      execPredicate(docs, propertyKey, (value) => {
        if (value == null) {
          return false
        }
        if (!Array.isArray(value)) {
          throw new Error('$size predicate requires array')
        }
        if (typeof o === 'number') {
          return value.length === o
        }
        if (typeof o === 'object' && o.$gt !== undefined) {
          return value.length > o.$gt
        }
        if (typeof o === 'object' && o.$gte !== undefined) {
          return value.length >= o.$gte
        }
        if (typeof o === 'object' && o.$lt !== undefined) {
          return value.length < o.$lt
        }
        if (typeof o === 'object' && o.$lte !== undefined) {
          return value.length <= o.$lte
        }
        return false
      })

View on GitHub (pinned to 63e28dc964)