{"record":{"id":"b663d4914a8dd819","repo":"chroma-core/chroma","slug":"unauthorized","errorCode":null,"errorMessage":"Unauthorized","messagePattern":"Unauthorized","errorType":"http","errorClass":"ChromaUnauthorizedError","httpStatus":401,"severity":"error","filePath":"clients/js/packages/chromadb-core/src/ChromaFetch.ts","lineNumber":67,"sourceCode":"  init?: RequestInit,\n): Promise<Response> => {\n  try {\n    const resp = await fetch(input, init);\n\n    const clonedResp = resp.clone();\n    const respBody = await clonedResp.json();\n    if (!clonedResp.ok) {\n      const error = createErrorByType(respBody?.error, respBody?.message);\n      if (error) {\n        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          }","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/js/packages/chromadb-core/src/ChromaFetch.ts#L49-L85","documentation":"_parse_k_aggregate() (operator.py:1355-1357) requires the value under the '$min_k' or '$max_k' operator key to be a dict describing the aggregation, e.g. {\"$min_k\": {\"keys\": [\"#score\"], \"k\": 3}}. If that value is a list, number, string, or None, the aggregation parameters cannot be extracted and a TypeError naming the operator is raised. This fires inside Aggregate.from_dict after it has already verified the outer dict holds exactly one recognized operator key (operator.py:1415-1427).","triggerScenarios":"GroupBy aggregate written as {\"$min_k\": [\"#score\", 3]} — a positional list instead of a field dict; {\"$min_k\": 3} — only the k value supplied; {\"$max_k\": null} in JSON/YAML config; {\"$min_k\": \"#score\"} — collapsing keys into a scalar because there is a single key.","commonSituations":"Authors abbreviating single-key aggregations and dropping the inner braces; YAML configs losing the nested mapping when re-indented; JSON built with jq/pydantic where the aggregation body was flattened; migrating from a shorthand API (MinK(keys=K.SCORE, k=3)) to dict payloads and assuming arguments map positionally.","solutions":["Wrap the operator's arguments in an inner dict with named fields: {\"$min_k\": {\"keys\": [\"#score\"], \"k\": 3}}.","Keep 'keys' a list even for one key: {\"keys\": [\"#score\"]}, and 'k' an int.","Prefer constructing typed objects when in doubt: GroupBy(keys=[\"category\"], aggregate=MinK(keys=K.SCORE, k=3)) — dicts are only the serialized form.","Lint nested YAML/JSON aggregations: the operator key's value must be an object, never a scalar/array."],"exampleFix":"// before\naggregate = {\"$min_k\": [\"#score\", 3]}   # TypeError: $min_k requires a dict, got list\n\n# after\naggregate = {\"$min_k\": {\"keys\": [\"#score\"], \"k\": 3}}","handlingStrategy":"type-guard","validationCode":"K_OPS = {\"$min_k\", \"$max_k\"}\ndef agg_body_is_dict(payload: dict) -> bool:\n    return (\n        isinstance(payload, dict)\n        and len(payload) == 1\n        and next(iter(payload)) in K_OPS\n        and isinstance(payload[next(iter(payload))], dict)\n    )","typeGuard":"def is_k_aggregate_payload(v: Any) -> TypeGuard[Dict[str, Dict[str, Any]]]:\n    if not (isinstance(v, dict) and len(v) == 1):\n        return False\n    op = next(iter(v))\n    return op in {\"$min_k\", \"$max_k\"} and isinstance(v[op], dict)","tryCatchPattern":"try:\n    Aggregate.from_dict(agg)\nexcept TypeError as e:\n    if \"requires a dict\" in str(e):\n        op = next(iter(agg))\n        agg = {op: agg[op] if isinstance(agg[op], dict) else {\"keys\": [\"#score\"], \"k\": 3}}\n    else:\n        raise","preventionTips":["Remember the three nesting levels: operator -> {keys, k} -> values.","Single-key aggregations still need the inner braces and a one-element keys list.","Build aggregates from MinK(keys=..., k=...).to_dict() when unsure of the shape."],"tags":["chromadb","aggregate","groupby","min-k","deserialization","typeerror"],"backgroundTag":"dict-schema-validation-failed","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}