{"record":{"id":"81c4121ecd5d9bc2","repo":"BerriAI/litellm","slug":"key-ids-must-be-provided-when-all-keys-in-team-is","errorCode":null,"errorMessage":"key_ids must be provided when all_keys_in_team is False","messagePattern":"key_ids must be provided when all_keys_in_team is False","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"litellm/proxy/management_endpoints/key_management_endpoints.py","lineNumber":3179,"sourceCode":"                \"AND\": [\n                    {\"OR\": [{\"blocked\": False}, {\"blocked\": None}]},\n                    {\"OR\": [{\"expires\": None}, {\"expires\": {\"gt\": now}}]},\n                ],\n            },\n            order={\"token\": \"asc\"},\n            take=MAX_BATCH_SIZE + 1,\n        )\n        if len(existing_keys) > MAX_BATCH_SIZE:\n            raise HTTPException(\n                status_code=400,\n                detail={\n                    \"error\": f\"Team {data.team_id} has more than {MAX_BATCH_SIZE} keys. Use `key_ids` to update in batches of {MAX_BATCH_SIZE}.\"\n                },\n            )\n        requested_tokens = [row.token for row in existing_keys]\n    else:\n        if data.key_ids is None or len(data.key_ids) == 0:\n            raise HTTPException(\n                status_code=400,\n                detail={\"error\": \"key_ids must be provided when all_keys_in_team is False\"},\n            )\n        # Dedupe by hashed form — duplicates collapse to one update.\n        requested_tokens = []\n        hashed_key_ids: Final = []\n        seen_hashes: Final = set()\n        for k in data.key_ids:\n            h = _hash_token_if_needed(k)\n            if h in seen_hashes:\n                continue\n            seen_hashes.add(h)\n            requested_tokens.append(k)\n            hashed_key_ids.append(h)\n        existing_keys = await VerificationTokenRepository(prisma_client).table.find_many(\n            where={\"team_id\": data.team_id, \"token\": {\"in\": hashed_key_ids}}\n        )\n","sourceCodeStart":3161,"sourceCodeEnd":3197,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/management_endpoints/key_management_endpoints.py#L3161-L3197","documentation":"POST /team/key/bulk_update requires exactly one selection mode: either all_keys_in_team=true (update every active key in the team) or an explicit non-empty key_ids list. When all_keys_in_team is false/absent and key_ids is null or empty, there is nothing to update and the endpoint rejects the request with a 400 before touching the database.","triggerScenarios":"POST /team/key/bulk_update with a body like {\"team_id\": \"my-team\"} or {\"team_id\": \"my-team\", \"key_ids\": []} and no all_keys_in_team flag; serializing a request model where key_ids defaulted to an empty list.","commonSituations":"A generic update helper that builds the payload dynamically and ends up with an empty list (e.g. a filter matched no keys); frontend sending the form before the key selection was populated; copy-pasting the curl example and forgetting the key_ids field.","solutions":["Add a non-empty key_ids array (<=500 entries) to the request body.","Or set \"all_keys_in_team\": true when you really mean every active key in the team.","Guard your calling code: skip the API call entirely when the computed key list is empty."],"exampleFix":"# before\npayload = {\"team_id\": tid, \"update_fields\": fields}\nif selected_keys:\n    payload[\"key_ids\"] = selected_keys\nclient.post(\"/team/key/bulk_update\", json=payload)  # fires 400 when selected_keys empty\n\n# after\nif not selected_keys:\n    log.info(\"no keys selected; skipping bulk update\")\nelse:\n    client.post(\"/team/key/bulk_update\", json={\"team_id\": tid, \"key_ids\": selected_keys, \"update_fields\": fields})","handlingStrategy":"validation","validationCode":"def build_bulk_update_body(team_id: str, key_ids: list[str] | None, all_keys: bool) -> dict:\n    if all_keys:\n        return {\"team_id\": team_id, \"all_keys_in_team\": True}\n    if not key_ids:\n        raise ValueError(\"key_ids must be a non-empty list when all_keys_in_team is false\")\n    return {\"team_id\": team_id, \"key_ids\": key_ids}","typeGuard":"def is_complete_bulk_request(key_ids: list[str] | None, all_keys_in_team: bool | None) -> bool:\n    return bool(all_keys_in_team) or (key_ids is not None and len(key_ids) > 0)","tryCatchPattern":null,"preventionTips":["Construct the request body through one validated builder function instead of ad-hoc dicts.","Skip the call when your selection logic yields zero keys rather than sending an empty payload.","Cover this in contract tests so a renamed field fails loudly at build time."],"tags":["validation","required-field","key-management","bulk-update","litellm-proxy"],"backgroundTag":"missing-required-field","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}