{"record":{"id":"17bb95f39a64b4c1","repo":"BerriAI/litellm","slug":"key-key-token-has-a-rpm-limit-key-rpm-limit-wh","errorCode":null,"errorMessage":"Key={key.token} has a rpm_limit={key.rpm_limit} which is greater than the team's rpm_limit={team.rpm_limit}.","messagePattern":"Key=(.+?) has a rpm_limit=(.+?) which is greater than the team's rpm_limit=(.+?)\\.","errorType":"http","errorClass":"HTTPException","httpStatus":403,"severity":"error","filePath":"litellm/proxy/management_endpoints/key_management_endpoints.py","lineNumber":3317,"sourceCode":"            # Skip special sentinel values — \"all-team-models\" means\n            # \"use whatever the team allows\", so it's always valid.\n            if model == SpecialModelNames.all_team_models.value:\n                continue\n            await can_team_access_model(\n                model=model,\n                team_object=team,\n                llm_router=llm_router,\n            )\n\n    # Check if the key's tpm/rpm limit is less than the team's tpm/rpm limit\n    if key.tpm_limit is not None:\n        if team.tpm_limit and key.tpm_limit > team.tpm_limit:\n            raise HTTPException(\n                status_code=403,\n                detail=f\"Key={key.token} has a tpm_limit={key.tpm_limit} which is greater than the team's tpm_limit={team.tpm_limit}.\",\n            )\n        if team.rpm_limit and key.rpm_limit and key.rpm_limit > team.rpm_limit:\n            raise HTTPException(\n                status_code=403,\n                detail=f\"Key={key.token} has a rpm_limit={key.rpm_limit} which is greater than the team's rpm_limit={team.rpm_limit}.\",\n            )\n\n    # Check if the key's user_id is a member of the team\n    member_object: Final = _get_user_in_team(team_table=cast(LiteLLM_TeamTableCachedObj, team), user_id=key.user_id)\n    if key.user_id is not None:\n        if not member_object:\n            raise HTTPException(\n                status_code=403,\n                detail=f\"User={key.user_id} is not a member of the team={team.team_id}. Check team members via `/team/info`.\",\n            )\n\n    # Check if the person initiating the change is a Proxy Admin or Team Admin\n    if (\n        change_initiated_by.user_role == LitellmUserRoles.PROXY_ADMIN.value\n        or _is_user_team_admin(\n            user_api_key_dict=change_initiated_by,","sourceCodeStart":3299,"sourceCodeEnd":3335,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/management_endpoints/key_management_endpoints.py#L3299-L3335","documentation":"Same family as the tpm check: a key's rpm_limit may not exceed its team's rpm_limit. The guard lives with the other team-key limit checks (note the nesting — it only runs inside `if key.tpm_limit is not None`, i.e. when the key defines a tpm_limit) and returns 403 naming the key, its rpm_limit, and the team's rpm_limit.","triggerScenarios":"POST /key/generate or /key/update with team_id set, a tpm_limit defined on the key, and key rpm_limit > team rpm_limit (e.g. team rpm_limit=100, key rpm_limit=1000).","commonSituations":"Burst-tolerance tuning: giving a service key a high rpm_limit while the team still has the default low cap; syncing key settings from a spreadsheet where team and key limits drifted; also note the asymmetric nesting means a key with only rpm_limit set (tpm_limit null) skips this check entirely — a validation gap to be aware of.","solutions":["Raise the team's rpm_limit (POST /team/update) to >= the key's rpm_limit, then retry the key creation/update.","Or lower the key's rpm_limit to <= the team's rpm_limit.","Or omit rpm_limit on the key to inherit the team's allowance."],"exampleFix":"# before\nclient.post(\"/key/generate\", json={\"team_id\": tid, \"rpm_limit\": 1000})\n\n# after\nclient.post(\"/team/update\", json={\"team_id\": tid, \"rpm_limit\": 1000})\nclient.post(\"/key/generate\", json={\"team_id\": tid, \"rpm_limit\": 1000})","handlingStrategy":"validation","validationCode":"def assert_key_rpm_within_team(client, team_id: str, rpm_limit: int | None) -> None:\n    if rpm_limit is None:\n        return\n    team = client.get(\"/team/info\", params={\"team_id\": team_id}).json()\n    team_rpm = team.get(\"rpm_limit\")\n    if team_rpm is not None and rpm_limit > team_rpm:\n        raise ValueError(f\"key rpm_limit {rpm_limit} > team rpm_limit {team_rpm}; raise team limit first\")","typeGuard":"def key_rpm_fits_team(key_rpm: int | None, team_rpm: int | None) -> bool:\n    return key_rpm is None or team_rpm is None or key_rpm <= team_rpm","tryCatchPattern":null,"preventionTips":["Validate both tpm and rpm against team limits together — they're enforced as a pair.","When raising burst capacity, update the team's rpm_limit in the same change as the key's.","Omit per-key rpm_limit unless you specifically need a tighter cap than the team."],"tags":["rate-limit","rpm","key-management","team-limits","litellm-proxy"],"backgroundTag":"quota-limit-exceeded","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}