{"record":{"id":"ff501fa8680fa344","repo":"hcengineering/platform","slug":"size-predicate-requires-array","errorCode":null,"errorMessage":"$size predicate requires array","messagePattern":"\\$size predicate requires array","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"foundations/core/packages/core/src/predicate.ts","lineNumber":121,"sourceCode":"  },\n  $lte: (o, propertyKey) => {\n    return (docs) => execPredicate(docs, propertyKey, (value) => value <= o)\n  },\n  $exists: (o, propertyKey) => {\n    return (docs) => execPredicate(docs, propertyKey, (value) => (value !== undefined) === o)\n  },\n  $ne: (o, propertyKey) => {\n    // eslint-disable-next-line eqeqeq\n    return (docs) => execPredicate(docs, propertyKey, (value) => (o != null ? !deepEqual(o, value) : value != null))\n  },\n  $size: (o, propertyKey) => {\n    return (docs) =>\n      execPredicate(docs, propertyKey, (value) => {\n        if (value == null) {\n          return false\n        }\n        if (!Array.isArray(value)) {\n          throw new Error('$size predicate requires array')\n        }\n        if (typeof o === 'number') {\n          return value.length === o\n        }\n        if (typeof o === 'object' && o.$gt !== undefined) {\n          return value.length > o.$gt\n        }\n        if (typeof o === 'object' && o.$gte !== undefined) {\n          return value.length >= o.$gte\n        }\n        if (typeof o === 'object' && o.$lt !== undefined) {\n          return value.length < o.$lt\n        }\n        if (typeof o === 'object' && o.$lte !== undefined) {\n          return value.length <= o.$lte\n        }\n        return false\n      })","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/core/packages/core/src/predicate.ts#L103-L139","documentation":"$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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the data so the field is always an array (migrate single values to [value]).","Ensure write paths always assign arrays, even for one element.","Pre-filter or $exists-guard the field, or use a different predicate ($exists, $ne) for non-array fields.","Wrap query execution in try/catch if heterogeneous legacy data must be tolerated."],"exampleFix":"// before (writer)\nawait update(doc, { $set: { attachments: file } })\n// after\nawait update(doc, { $set: { attachments: [file] } })","handlingStrategy":"validation","validationCode":"// Before querying with $size, ensure the field holds arrays everywhere\nconst bad = docs.find((d) => d.attachments != null && !Array.isArray(d.attachments))\nif (bad != null) throw new Error(`doc ${bad._id} has non-array attachments; migrate before using $size`)","typeGuard":"function hasArrayField<K extends string>(doc: Record<string, unknown>, key: K): doc is Record<K, unknown[]> {\n  return Array.isArray(doc[key])\n}","tryCatchPattern":"try {\n  return await find(clazz, { attachments: { $size: 3 } })\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('$size predicate requires array')) {\n    // fall back: filter in code over non-array-tolerant predicate\n    const all = await find(clazz, { attachments: { $exists: true } })\n    return all.filter((d) => Array.isArray(d.attachments) && d.attachments.length === 3)\n  }\n  throw e\n}","preventionTips":["Enforce array types on the field in every write path, even for single elements.","Migrate legacy docs that store scalars/objects in the field before using $size.","Prefer $exists/$ne predicates for fields that may be non-arrays."],"tags":["query","predicate","data-shape"],"backgroundTag":"predicate-requires-array","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}