{"record":{"id":"227e3f9b68f7cbb1","repo":"chroma-core/chroma","slug":"the-resource-already-exists","errorCode":null,"errorMessage":"The resource already exists","messagePattern":"The resource already exists","errorType":"http","errorClass":"ChromaUniqueError","httpStatus":409,"severity":"error","filePath":"clients/js/packages/chromadb-core/src/ChromaFetch.ts","lineNumber":77,"sourceCode":"        throw error;\n      }\n      switch (resp.status) {\n        case 400:\n          throw new ChromaClientError(\n            `Bad request to ${input} with status: ${resp.statusText}`,\n          );\n        case 401:\n          throw new ChromaUnauthorizedError(`Unauthorized`);\n        case 403:\n          throw new ChromaForbiddenError(\n            `You do not have permission to access the requested resource.`,\n          );\n        case 404:\n          throw new ChromaNotFoundError(\n            `The requested resource could not be found: ${input}`,\n          );\n        case 409:\n          throw new ChromaUniqueError(\"The resource already exists\");\n        case 422:\n          if (\n            respBody?.message &&\n            (respBody?.message.startsWith(\"Quota exceeded\") ||\n              respBody?.message.startsWith(\"Billing limit exceeded\"))\n          ) {\n            throw new ChromaQuotaExceededError(respBody?.message);\n          }\n          break;\n        case 500:\n          throw parseServerError(respBody?.error);\n        case 502:\n        case 503:\n        case 504:\n          throw new ChromaConnectionError(\n            `Unable to connect to the chromadb server. Please try again later.`,\n          );\n      }","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/js/packages/chromadb-core/src/ChromaFetch.ts#L59-L95","documentation":"_parse_k_aggregate() (operator.py:1363-1365) requires the 'keys' field inside a '$min_k'/'$max_k' aggregation to be a list or tuple; anything else raises TypeError with the offending type name. The aggregation ranks records on these keys per group (e.g. [\"#score\"] or [\"priority\", \"#score\"]), and _strings_to_keys (operator.py:1334-1336) iterates it element-wise — a scalar or dict cannot supply an ordered ranking key set. Unlike Select.from_dict, a set is not accepted here because multi-key aggregation order is significant.","triggerScenarios":"{\"$min_k\": {\"keys\": \"#score\", \"k\": 3}} — single string not wrapped in a list; {\"keys\": {\"key\": \"#score\"}} — object form from JSON config; {\"keys\": null}; passing a Python set — accepted by Select.from_dict but rejected here, so reusing a select payload's keys for the aggregate fails.","commonSituations":"Single-key aggregations written without brackets; JSON configs using an object because a config system cannot express empty/typed arrays; single-element arrays collapsed to scalars by intermediate serializers; porting a Select payload (where set/tuple are fine) into an aggregate.","solutions":["Wrap keys in a list: {\"$min_k\": {\"keys\": [\"#score\"], \"k\": 3}}.","If you hold a Python set from a Select, convert it: {\"keys\": list(select_keys)} before embedding it in an aggregate.","Use '#score' (mapped to Key.SCORE by _strings_to_keys) or plain metadata field names as string elements.","For multi-key ranking keep the order meaningful: [\"priority\", \"#score\"] ranks by priority first, score as tiebreaker."],"exampleFix":"// before\naggregate = {\"$min_k\": {\"keys\": \"#score\", \"k\": 3}}   # TypeError: $min_k keys must be a list, got str\n\n# after\naggregate = {\"$min_k\": {\"keys\": [\"#score\"], \"k\": 3}}","handlingStrategy":"type-guard","validationCode":"def agg_keys_is_list(payload: dict) -> bool:\n    op = next(iter(payload))\n    body = payload.get(op, {})\n    return isinstance(body, dict) and isinstance(body.get(\"keys\"), (list, tuple))","typeGuard":"def is_wellformed_k_aggregate(v: Any) -> TypeGuard[Dict[str, Dict[str, Any]]]:\n    if not (isinstance(v, dict) and len(v) == 1):\n        return False\n    op, body = next(iter(v.items()))\n    return (\n        op in {\"$min_k\", \"$max_k\"}\n        and isinstance(body, dict)\n        and isinstance(body.get(\"keys\"), (list, tuple))\n        and bool(body.get(\"keys\"))\n        and isinstance(body.get(\"k\"), int)\n        and not isinstance(body.get(\"k\"), bool)\n        and body[\"k\"] > 0\n    )","tryCatchPattern":"try:\n    Aggregate.from_dict(agg)\nexcept TypeError as e:\n    if \"keys must be a list\" in str(e):\n        op = next(iter(agg))\n        ks = agg[op][\"keys\"]\n        agg[op][\"keys\"] = [ks] if isinstance(ks, str) else list(ks)\n    else:\n        raise","preventionTips":["Aggregate keys accept list/tuple only — sets are legal for Select but not here (order matters).","Convert collections from Select contexts: list(...) before reuse.","Bracket single keys: ['#score'], not '#score'."],"tags":["chromadb","aggregate","keys","typeerror","min-k","max-k"],"backgroundTag":"dict-schema-validation-failed","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}