{"record":{"id":"ba045523b9ab5d3a","repo":"hcengineering/platform","slug":"in-predicate-requires-array","errorCode":null,"errorMessage":"$in predicate requires array","messagePattern":"\\$in predicate requires array","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"foundations/core/packages/core/src/predicate.ts","lineNumber":42,"sourceCode":"type PredicateFactory = (pred: any, propertyKey: string) => Predicate\n\ntype ExecPredicate = (value: any) => boolean\n\nfunction execPredicate (docs: Doc[], propertyKey: string, pred: ExecPredicate): Doc[] {\n  const result: Doc[] = []\n  for (const doc of docs) {\n    const value = getObjectValue(propertyKey, doc)\n    if (pred(value)) {\n      result.push(doc)\n    }\n  }\n  return result\n}\n\nconst predicates: Record<string, PredicateFactory> = {\n  $in: (o, propertyKey) => {\n    if (!Array.isArray(o)) {\n      throw new Error('$in predicate requires array')\n    }\n    return (docs) =>\n      execPredicate(docs, propertyKey, (value) => {\n        if (Array.isArray(value)) {\n          return o.some((p) => value.includes(p))\n        } else {\n          // eslint-disable-next-line eqeqeq\n          return o.some((p) => p == value)\n        }\n      })\n  },\n  $all: (o, propertyKey) => {\n    if (!Array.isArray(o)) {\n      throw new Error('$all predicate requires array')\n    }\n    return (docs) =>\n      execPredicate(docs, propertyKey, (value: any[]) => {\n        for (const val of o) {","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/core/packages/core/src/predicate.ts#L24-L60","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wrap the value in an array: { $in: [value] } instead of { $in: value }.","Guard dynamic values with Array.isArray before building the query and normalize single values to [value].","Check upstream code that produces the list (API call, map/filter) to ensure it returns an array, not undefined."],"exampleFix":"// before\nconst q = { status: { $in: statusesFromApi } }\n// after\nconst list = Array.isArray(statusesFromApi) ? statusesFromApi : statusesFromApi != null ? [statusesFromApi] : []\nconst q = { status: { $in: list } }","handlingStrategy":"type-guard","validationCode":"function asArray<T>(v: T | T[] | undefined | null): T[] {\n  return Array.isArray(v) ? v : v != null ? [v] : []\n}\nconst q = { status: { $in: asArray(statuses) } }","typeGuard":"function isArrayOrThrow<T>(v: unknown): asserts v is T[] {\n  if (!Array.isArray(v)) throw new TypeError('$in expects an array of values')\n}","tryCatchPattern":"try {\n  return await find(clazz, query)\n} catch (e) {\n  if (e instanceof Error && e.message.includes('predicate requires array')) {\n    return await find(clazz, normalizeQueryArrays(query))\n  }\n  throw e\n}","preventionTips":["Always normalize single values to arrays before building $in clauses.","Type query-builder inputs as readonly T[] so scalars fail at compile time.","Check API/config paths that supply lists for undefined before querying."],"tags":["query","predicate","type-error"],"backgroundTag":"predicate-requires-array","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}