{"record":{"id":"e01fdda47dc7dbd6","repo":"BerriAI/litellm","slug":"key-key-token-has-a-tpm-limit-key-tpm-limit-wh","errorCode":null,"errorMessage":"Key={key.token} has a tpm_limit={key.tpm_limit} which is greater than the team's tpm_limit={team.tpm_limit}.","messagePattern":"Key=(.+?) has a tpm_limit=(.+?) which is greater than the team's tpm_limit=(.+?)\\.","errorType":"http","errorClass":"HTTPException","httpStatus":403,"severity":"error","filePath":"litellm/proxy/management_endpoints/key_management_endpoints.py","lineNumber":3312,"sourceCode":"    - The person initiating the change must be either Proxy Admin or Team Admin\n    \"\"\"\n    # Check if the team has access to the key's models\n    if len(key.models) > 0:\n        for model in key.models:\n            # 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","sourceCodeStart":3294,"sourceCodeEnd":3330,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/management_endpoints/key_management_endpoints.py#L3294-L3330","documentation":"LiteLLM enforces that a virtual key's tpm_limit can never exceed the tpm_limit of the team it belongs to (a key is a slice of the team's allowance, not an expansion of it). This check runs on key generation and key update paths for team-scoped keys and returns 403 with the offending key token and both limits. The key token is included in the message, which is a mild information-disclosure consideration in shared logs.","triggerScenarios":"POST /key/generate or /key/update with team_id set and tpm_limit greater than that team's tpm_limit (e.g. team tpm_limit=100000, key tpm_limit=500000); bulk team key update that raises tpm_limit on keys of a team whose own limit is lower.","commonSituations":"Copying key limits from another environment/team without aligning team limits first; raising a team's per-key budgets in config but forgetting to raise the team budget; onboarding scripts that assign a default tpm_limit per key larger than small teams' caps.","solutions":["Raise the team's tpm_limit first (POST /team/update) to at least the desired key limit, then set the key's tpm_limit.","Or lower the key's tpm_limit to <= the team's tpm_limit.","Set tpm_limit=null on the key to inherit 'whatever the team allows', which is always valid."],"exampleFix":"# before\nclient.post(\"/key/generate\", json={\"team_id\": tid, \"tpm_limit\": 500000})\n\n# after\nclient.post(\"/team/update\", json={\"team_id\": tid, \"tpm_limit\": 500000})\nclient.post(\"/key/generate\", json={\"team_id\": tid, \"tpm_limit\": 500000})","handlingStrategy":"validation","validationCode":"def assert_key_limit_within_team(client, team_id: str, tpm_limit: int | None) -> None:\n    if tpm_limit is None:\n        return\n    team = client.get(\"/team/info\", params={\"team_id\": team_id}).json()\n    team_tpm = team.get(\"tpm_limit\")\n    if team_tpm is not None and tpm_limit > team_tpm:\n        raise ValueError(f\"key tpm_limit {tpm_limit} > team tpm_limit {team_tpm}; raise team limit first\")","typeGuard":"def key_tpm_fits_team(key_tpm: int | None, team_tpm: int | None) -> bool:\n    return key_tpm is None or team_tpm is None or key_tpm <= team_tpm","tryCatchPattern":null,"preventionTips":["Make 'raise team limit, then key limit' the standard order in every provisioning script.","Store team limits alongside key limits in your config source so they can't drift apart.","Prefer leaving key tpm_limit unset to inherit the team's cap."],"tags":["rate-limit","tpm","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"}