diff --git a/litellm/proxy/credential_endpoints/endpoints.py b/litellm/proxy/credential_endpoints/endpoints.py index 20984f3a91c..3b2752f4cf9 100644 --- a/litellm/proxy/credential_endpoints/endpoints.py +++ b/litellm/proxy/credential_endpoints/endpoints.py @@ -7,7 +7,7 @@ from typing import ( cast, # noqa: TID251 # jsonify_object in proxy/utils.py is annotated with a bare dict ) -from fastapi import APIRouter, Depends, HTTPException, Path, Request, Response +from fastapi import APIRouter, Depends, HTTPException, Path, Request, Response, status import litellm from litellm._logging import verbose_proxy_logger @@ -23,7 +23,13 @@ from litellm.llms.base_llm.auth.identity_source import ( InternalIssuerSource, ) from litellm.llms.base_llm.auth.internal_issuer import internal_issuer_jwks_document -from litellm.proxy._types import CommonProxyErrors, LitellmUserRoles, UserAPIKeyAuth +from litellm.proxy._types import ( + CommonProxyErrors, + LitellmUserRoles, + ProxyErrorTypes, + ProxyException, + UserAPIKeyAuth, +) from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.common_utils.credential_hydration import ( hydrate_named_credential, @@ -51,14 +57,13 @@ def _reject_non_admin_wif_fields( """ if not wif_fields or user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN: return - raise HTTPException( - status_code=403, - detail={ # mutable-ok: starlette json.dumps()s HTTPException.detail raw, needs a real dict - "error": ( - f"Only proxy admins can change {wif_fields[0]!r}, a server-owned workload identity federation " - "parameter." - ) - }, + raise ProxyException( + message=( + f"Only proxy admins can change {wif_fields[0]!r}, a server-owned workload identity federation parameter." + ), + type=ProxyErrorTypes.auth_error.value, + code=status.HTTP_403_FORBIDDEN, + param=wif_fields[0], ) @@ -422,8 +427,6 @@ async def delete_credential( ## DELETE FROM LITELLM ## litellm.credential_list = [cred for cred in litellm.credential_list if cred.credential_name != credential_name] return {"success": True, "message": "Credential deleted successfully"} - except HTTPException: - raise except Exception as e: raise handle_exception_on_proxy(e) diff --git a/tests/test_litellm/proxy/credential_endpoints/test_endpoints.py b/tests/test_litellm/proxy/credential_endpoints/test_endpoints.py index da679fbe6e6..fb619356e88 100644 --- a/tests/test_litellm/proxy/credential_endpoints/test_endpoints.py +++ b/tests/test_litellm/proxy/credential_endpoints/test_endpoints.py @@ -742,7 +742,7 @@ class TestNonAdminCannotTouchAStoredWifCredential: response = _delete_credential("federated-cred", auth=_as_non_admin) assert response.status_code == 403, response.text - assert "anthropic_keycloak_token_url" in response.text + assert response.json()["error"]["param"] == "anthropic_keycloak_token_url" repository.delete_by_name.assert_not_awaited() def test_a_stale_in_memory_copy_does_not_authorize_deleting_a_stored_wif_credential( @@ -757,7 +757,7 @@ class TestNonAdminCannotTouchAStoredWifCredential: response = _delete_credential("federated-cred", auth=_as_non_admin) assert response.status_code == 403, response.text - assert "anthropic_keycloak_token_url" in response.text + assert response.json()["error"]["param"] == "anthropic_keycloak_token_url" repository.delete_by_name.assert_not_awaited() def test_proxy_admin_can_delete_a_stored_wif_credential(self, restore_credential_list): @@ -839,7 +839,7 @@ class TestNonAdminCannotTouchAStoredWifCredential: response = _delete_credential("config-wif", auth=_as_non_admin) assert response.status_code == 403, response.text - assert "anthropic_keycloak_token_url" in response.text + assert response.json()["error"]["param"] == "anthropic_keycloak_token_url" repository.delete_by_name.assert_not_awaited() assert litellm.credential_list == [config_credential] @@ -1053,7 +1053,7 @@ def test_delete_credential_answers_404_when_the_credential_does_not_exist(creden response = _delete_credential("definitely-not-there") assert response.status_code == 404, f"delete of a missing credential answered {response.status_code}: {response.text}" - assert "definitely-not-there" in response.text + assert "definitely-not-there" in response.json()["error"]["message"] def test_delete_credential_still_answers_200_and_drops_the_credential_from_memory(credential_store):