BerriAI/litellm · error · HTTPException
Key not found in team {data.team_id}
Error message
Key not found in team {data.team_id} What it means
During POST /team/key/bulk_update in key_ids mode, LiteLLM first loads the team's existing key rows into a token->row map; a requested key whose (hashed) token is not in that map is recorded as a failed update with this 404 message. The key either belongs to a different team, was deleted, or was mistyped — and because the update runs per key inside a loop, this failure is collected per key rather than aborting the whole batch.
Source
Thrown at litellm/proxy/management_endpoints/key_management_endpoints.py:3237
_check_passthrough_routes_caller_permission(data=data.update_fields, user_api_key_dict=user_api_key_dict)
if not requested_tokens:
raise HTTPException(
status_code=404,
detail={"error": f"No keys found for team {data.team_id}"},
)
existing_by_token: Final = {row.token: row for row in existing_keys}
update_field_dict: Final = data.update_fields.model_dump(exclude_unset=True)
successful_updates: Final[list[SuccessfulKeyUpdate]] = []
failed_updates: Final[list[FailedKeyUpdate]] = []
for token in requested_tokens:
db_token = _hash_token_if_needed(token)
try:
if db_token not in existing_by_token:
raise HTTPException(
status_code=404,
detail={"error": f"Key not found in team {data.team_id}"},
)
# team_id from validated scope, never user payload — drives _check_team_key_limits.
update_key_request = UpdateKeyRequest.model_validate(
{
"key": token,
"team_id": data.team_id,
**update_field_dict,
}
)
updated_key_info = await _process_single_key_update(
update_key_request=update_key_request,
user_api_key_dict=user_api_key_dict,
litellm_changed_by=litellm_changed_by,
prisma_client=prisma_client,
user_api_key_cache=user_api_key_cache,View on GitHub (pinned to 77b7c6c40c)
Solutions
- Inspect the failed_updates array in the BulkUpdateKeyResponse — only the listed keys failed; the rest were updated.
- Re-fetch the current team keys (GET /team/info or /v2/key/info) and rerun the bulk update with the intersection of your list and the live list.
- Make your listing and update steps short-lived so keys are less likely to disappear in between; treat per-key 404 as expected and prunable.
Example fix
# before
resp = client.post("/team/key/bulk_update", json={"team_id": tid, "key_ids": my_cached_ids, "update_fields": fields})
# after
live_ids = set(get_team_key_ids(tid))
usable = [k for k in my_cached_ids if k in live_ids]
resp = client.post("/team/key/bulk_update", json={"team_id": tid, "key_ids": usable, "update_fields": fields})
for f in resp.json().get("failed_updates", []):
log.warning("key %s failed: %s", f["key"], f["error"]) Defensive patterns
Strategy: try-catch
Validate before calling
live_tokens = set(fetch_team_key_ids(tid))
requestable = [k for k in my_key_ids if k in live_tokens]
dropped = set(my_key_ids) - live_tokens
if dropped:
log.warning("dropping %d keys no longer in team %s: %s", len(dropped), tid, dropped) Try / catch
# Per-key failures are returned in the response body, not thrown — inspect them:
result = client.post("/team/key/bulk_update", json={...}).json()
for fail in result.get("failed_updates", []):
if "Key not found in team" in str(fail.get("error", "")):
prune_from_local_cache(fail["key"])
else:
escalate(fail) Prevention
- Minimize the window between listing team keys and issuing the bulk update.
- Always process failed_updates from BulkUpdateKeyResponse; never assume full success.
- Drop keys that 404 from your local state instead of resending them next run.
When it happens
Trigger: Including a key token that was rotated/deleted between your listing call and the bulk update; passing keys that belong to another team; passing unhashed tokens when the DB stores hashed tokens after enabling KEY_DB checkpoints (note _hash_token_if_needed normalizes this, so the usual cause is wrong team or deleted key).
Common situations: Race between an admin deleting keys and your bulk job running; stale key list cached from an earlier export; copying key IDs between environments (dev key sent to prod proxy).
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- No keys found for team {data.team_id}
- Maximum {MAX_BATCH_SIZE} keys can be updated at once. Found
- Team {data.team_id} has more than {MAX_BATCH_SIZE} keys. Use
- key_ids must be provided when all_keys_in_team is False
- not_found_error
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/5c2ef9b32fa3795f.
Report an issue: GitHub.