{"record":{"id":"d141988be5ffa127","repo":"chroma-core/chroma","slug":"aggregate-input-must-be-an-aggregate-instance-or-o","errorCode":null,"errorMessage":"Aggregate input must be an Aggregate instance or object with $min_k or $max_k","messagePattern":"Aggregate input must be an Aggregate instance or object with \\$min_k or \\$max_k","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"clients/new-js/packages/chromadb/src/execution/expression/groupBy.ts","lineNumber":41,"sourceCode":"      return input;\n    }\n    if (isPlainObject(input)) {\n      if (\"$min_k\" in input) {\n        const data = input.$min_k as MinKJSON;\n        return new MinK(\n          data.keys.map((k) => new Key(k)),\n          data.k,\n        );\n      }\n      if (\"$max_k\" in input) {\n        const data = input.$max_k as MaxKJSON;\n        return new MaxK(\n          data.keys.map((k) => new Key(k)),\n          data.k,\n        );\n      }\n    }\n    throw new TypeError(\n      \"Aggregate input must be an Aggregate instance or object with $min_k or $max_k\",\n    );\n  }\n\n  public static minK(keys: (Key | string)[], k: number): MinK {\n    return new MinK(\n      keys.map((key) => (key instanceof Key ? key : new Key(key))),\n      k,\n    );\n  }\n\n  public static maxK(keys: (Key | string)[], k: number): MaxK {\n    return new MaxK(\n      keys.map((key) => (key instanceof Key ? key : new Key(key))),\n      k,\n    );\n  }\n}","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/new-js/packages/chromadb/src/execution/expression/groupBy.ts#L23-L59","documentation":"Aggregate.from() converts user input into a GroupBy aggregate (MinK or MaxK). It only accepts an Aggregate instance or a plain object in the serialized JSON shape { $min_k: { keys, k } } or { $max_k: { keys, k } }. Any other value — a bare object without the $min_k/$max_k envelope, a string, number, or array — raises this TypeError before the query is built.","triggerScenarios":"GroupBy.from({ keys: [\"category\"], aggregate: { keys: [\"score\"], k: 5 } }) — aggregate object missing the $min_k/$max_k wrapper. Passing a non-Aggregate class instance or a JSON blob whose aggregate shape drifted (e.g. restored from persistence or received from another service).","commonSituations":"Hand-writing the aggregate JSON and forgetting the $min_k/$max_k envelope; round-tripping serialized GroupBy JSON after a schema change; copying the GroupByJSON type's outer shape but nesting the keys/k directly under aggregate.","solutions":["Build aggregates with the static helpers Aggregate.minK(keys, k) or Aggregate.maxK(keys, k) instead of raw objects","If you must write raw JSON, wrap it correctly: { $min_k: { keys: [\"score\"], k: 5 } }","When deserializing persisted queries, validate that aggregate has a $min_k or $max_k key before calling GroupBy.from"],"exampleFix":"// before\nGroupBy.from({\n  keys: [\"category\"],\n  aggregate: { keys: [\"score\"], k: 5 }, // missing $min_k wrapper -> TypeError\n});\n\n// after\nGroupBy.from({\n  keys: [\"category\"],\n  aggregate: Aggregate.minK([\"score\"], 5), // or { $min_k: { keys: [\"score\"], k: 5 } }\n});","handlingStrategy":"type-guard","validationCode":"const isAggregateShape = (v: unknown): boolean =>\n  v instanceof Aggregate ||\n  (typeof v === \"object\" &&\n    v !== null &&\n    (\"$min_k\" in v || \"$max_k\" in v));\n\nif (!isAggregateShape(input.aggregate)) {\n  throw new Error(\"aggregate must be Aggregate.minK/maxK(...) or { $min_k | $max_k: { keys, k } }\");\n}\nconst gb = GroupBy.from(input as GroupByJSON);","typeGuard":"function isAggregateJSON(v: unknown): v is { $min_k: { keys: string[]; k: number } } | { $max_k: { keys: string[]; k: number } } {\n  if (typeof v !== \"object\" || v === null) return false;\n  const o = v as Record<string, unknown>;\n  const inner = (o.$min_k ?? o.$max_k) as { keys?: unknown; k?: unknown } | undefined;\n  return !!inner && Array.isArray(inner.keys) && typeof inner.k === \"number\";\n}","tryCatchPattern":"try {\n  const gb = GroupBy.from(queryJson);\n} catch (e) {\n  if (e instanceof TypeError && /Aggregate input/.test(e.message)) {\n    // rebuild aggregate with Aggregate.minK/maxK helpers instead of raw JSON\n  } else throw e;\n}","preventionTips":["Always construct aggregates via Aggregate.minK()/Aggregate.maxK() rather than hand-written JSON","Validate persisted/foreign JSON shapes at the deserialization boundary before feeding GroupBy.from","Remember the envelope keys are exactly $min_k and $max_k — no camelCase variants"],"tags":["chroma","group-by","aggregate","typeerror"],"backgroundTag":"schema-validation-failed","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}