{"record":{"id":"e04bf31194fef518","repo":"chroma-core/chroma","slug":"groupby-requires-keys-array","errorCode":null,"errorMessage":"GroupBy requires 'keys' array","messagePattern":"GroupBy requires 'keys' array","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"clients/new-js/packages/chromadb/src/execution/expression/groupBy.ts","lineNumber":130,"sourceCode":"    public readonly keys: Key[],\n    public readonly aggregate: Aggregate,\n  ) {\n    if (keys.length === 0) {\n      throw new Error(\"GroupBy keys cannot be empty\");\n    }\n  }\n\n  public static from(input: GroupByInput | undefined): GroupBy | undefined {\n    if (input === undefined || input === null) {\n      return undefined;\n    }\n    if (input instanceof GroupBy) {\n      return input;\n    }\n    if (isPlainObject(input)) {\n      const data = input as GroupByJSON;\n      if (!data.keys || !Array.isArray(data.keys)) {\n        throw new TypeError(\"GroupBy requires 'keys' array\");\n      }\n      if (!data.aggregate) {\n        throw new TypeError(\"GroupBy requires 'aggregate'\");\n      }\n      return new GroupBy(\n        data.keys.map((k) => new Key(k)),\n        Aggregate.from(data.aggregate),\n      );\n    }\n    throw new TypeError(\n      \"GroupBy input must be a GroupBy instance or plain object\",\n    );\n  }\n\n  public toJSON(): GroupByJSON {\n    return {\n      keys: this.keys.map((key) => key.name),\n      aggregate: this.aggregate.toJSON(),","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/new-js/packages/chromadb/src/execution/expression/groupBy.ts#L112-L148","documentation":"When GroupBy.from() receives a plain object (the serialized GroupByJSON shape), it requires a 'keys' property that is an Array. A missing keys field, or keys set to a string/number/null instead of an array, raises this TypeError before construction.","triggerScenarios":"GroupBy.from({ aggregate: { $min_k: { keys: [\"score\"], k: 5 } } }) — keys omitted. GroupBy.from({ keys: \"category\", aggregate }) — keys is a string, not an array.","commonSituations":"Hand-writing the group-by JSON and abbreviating keys. Partial JSON produced by destructuring or object spread that drops the keys field. Treating a single field name as acceptable (must be wrapped in an array).","solutions":["Include keys as an array of field names: { keys: [\"category\"], aggregate }","Wrap single field names in brackets: keys: [\"category\"], not keys: \"category\"","Prefer constructing GroupBy directly with Key instances instead of raw JSON"],"exampleFix":"// before\nGroupBy.from({ keys: \"category\", aggregate: Aggregate.minK([\"score\"], 5) }); // TypeError\n\n// after\nGroupBy.from({ keys: [\"category\"], aggregate: Aggregate.minK([\"score\"], 5) });","handlingStrategy":"type-guard","validationCode":"if (!Array.isArray(queryJson.keys) || queryJson.keys.length === 0) {\n  throw new Error(\"GroupBy JSON requires keys: string[] (wrap single names in an array)\");\n}\nconst gb = GroupBy.from(queryJson);","typeGuard":"function isGroupByJSON(v: unknown): v is { keys: string[]; aggregate: unknown } {\n  return (\n    typeof v === \"object\" &&\n    v !== null &&\n    Array.isArray((v as { keys?: unknown }).keys) &&\n    \"aggregate\" in v\n  );\n}","tryCatchPattern":"try {\n  const gb = GroupBy.from(payload);\n} catch (e) {\n  if (e instanceof TypeError && /requires 'keys'/.test(e.message)) {\n    // fix payload: keys must be an array like [\"category\"]\n  } else throw e;\n}","preventionTips":["Always write group-by keys as an array, even for one field","Validate deserialized JSON at the boundary with isGroupByJSON","Prefer building GroupBy from typed code over hand-written JSON"],"tags":["chroma","group-by","json","validation"],"backgroundTag":"missing-required-field","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}