BerriAI/litellm · error · HTTPException

Not all keys passed in were deleted. This probably means you

Error message

Not all keys passed in were deleted. This probably means you don't have access to delete all the keys passed in. Keys passed in={num_keys_to_be_deleted}, Deleted keys ={number_deleted_keys}

What it means

After deleting, /key/delete compares the number of keys you asked to delete with the number actually deleted. A mismatch means some keys were skipped by the ownership filter — the handler enforces that a non-admin caller can only delete keys they own (delete_verification_tokens scopes the where-clause by user). It surfaces as a 400 with both counts, telling you the request was only partially applied.

Source

Thrown at litellm/proxy/management_endpoints/key_management_endpoints.py:3434

            )
            num_keys_to_be_deleted = len(data.key_aliases)
            deleted_keys = data.key_aliases
        else:
            raise ValueError("Invalid request type")

        if number_deleted_keys is None:
            raise ProxyException(
                message="Failed to delete keys got None response from delete_verification_token",
                type=ProxyErrorTypes.internal_server_error,
                param="keys",
                code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )
        verbose_proxy_logger.debug("/key/delete - deleted_keys=%s", number_deleted_keys)

        try:
            assert num_keys_to_be_deleted == len(deleted_keys)
        except Exception:
            raise HTTPException(
                status_code=400,
                detail={
                    "error": f"Not all keys passed in were deleted. This probably means you don't have access to delete all the keys passed in. Keys passed in={num_keys_to_be_deleted}, Deleted keys ={number_deleted_keys}"
                },
            )

        verbose_proxy_logger.debug(
            "/keys/delete - cache after delete: %s", user_api_key_cache.in_memory_cache.cache_dict
        )

        asyncio.create_task(
            KeyManagementEventHooks.async_key_deleted_hook(
                data=data,
                keys_being_deleted=_keys_being_deleted,
                user_api_key_dict=user_api_key_dict,
                litellm_changed_by=litellm_changed_by,
                response=number_deleted_keys,
            )

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Re-authenticate with the proxy admin master key if you intend to delete keys you don't own.
  2. Filter your list to keys that still exist and that the caller owns before sending (verify via GET /key/info).
  3. Treat this as partial success: reconcile which keys remain and send a second request only for those you're allowed to delete.

Example fix

# before (user key deleting others' keys)
client.headers["Authorization"] = "Bearer sk-user-key"
client.post("/key/delete", json={"keys": all_team_keys})

# after (admin master key, existing keys only)
client.headers["Authorization"] = "Bearer sk-master"
existing = [k for k in all_team_keys if key_exists(k)]
client.post("/key/delete", json={"keys": existing})
Defensive patterns

Strategy: validation

Validate before calling

# Pre-filter to keys the caller can delete (exist + owned) when not using the master key
infos = client.post("/v2/key/info", json={"keys": candidate_keys}).json()
deletable = [k["token"] for k in infos if k and k.get("user_id") == caller_user_id]
client.post("/key/delete", json={"keys": deletable})

Try / catch

try:
    client.post("/key/delete", json={"keys": keys})
except HTTPError as e:
    if e.response.status_code == 400 and "Not all keys passed in were deleted" in e.response.text:
        remaining = reconcile_remaining_keys(keys)  # diff against /v2/key/info
        if remaining:
            escalate("delete requires admin key for foreign keys: %s", remaining)
    else:
        raise

Prevention

When it happens

Trigger: Calling POST /key/delete with keys belonging to other users/teams while authenticated with a non-admin key; including already-deleted keys (delete_many counts only rows removed this call); mixing valid and invalid tokens in one request.

Common situations: A cleanup script with a stale key list where some keys were already deleted; a team member trying to purge all team keys without proxy-admin credentials; deleting a key owned by a service account from a personal key.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/0112eb13a6c18035. Report an issue: GitHub.