Skip to content

Commit dc76b6c

Browse files
authored
[Fix] Management Endpoints - Fixes inconsistent error responses in customer management endpoints. Non-existent user errors now return proper 404 status codes with consistent error schema format across all endpoints. (#16450)
* fix: ensure end user endpoints use "handle_exception_on_proxy" correctly * test 404 on info and update for non-existent user * test 404 for no customer found * fix 404 handling for customer endpoints * test_error_schema_consistency * test_customer_endpoints_error_schema_consistency
1 parent 656dce9 commit dc76b6c

File tree

2 files changed

+317
-116
lines changed

2 files changed

+317
-116
lines changed

litellm/proxy/management_endpoints/customer_endpoints.py

Lines changed: 88 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from litellm._logging import verbose_proxy_logger
2121
from litellm.proxy._types import *
2222
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
23+
from litellm.proxy.utils import handle_exception_on_proxy
2324

2425
router = APIRouter()
2526

@@ -305,22 +306,7 @@ async def new_end_user(
305306
code=400,
306307
param="user_id",
307308
)
308-
309-
if isinstance(e, HTTPException):
310-
raise ProxyException(
311-
message=getattr(e, "detail", f"Internal Server Error({str(e)})"),
312-
type="internal_error",
313-
param=getattr(e, "param", "None"),
314-
code=getattr(e, "status_code", status.HTTP_500_INTERNAL_SERVER_ERROR),
315-
)
316-
elif isinstance(e, ProxyException):
317-
raise e
318-
raise ProxyException(
319-
message="Internal Server Error, " + str(e),
320-
type="internal_error",
321-
param=getattr(e, "param", "None"),
322-
code=status.HTTP_500_INTERNAL_SERVER_ERROR,
323-
)
309+
raise handle_exception_on_proxy(e)
324310

325311

326312
@router.get(
@@ -352,25 +338,35 @@ async def end_user_info(
352338
-H 'Authorization: Bearer sk-1234'
353339
```
354340
"""
355-
from litellm.proxy.proxy_server import prisma_client
356-
357-
if prisma_client is None:
358-
raise HTTPException(
359-
status_code=500,
360-
detail={"error": CommonProxyErrors.db_not_connected_error.value},
361-
)
341+
try:
342+
from litellm.proxy.proxy_server import prisma_client
362343

363-
user_info = await prisma_client.db.litellm_endusertable.find_first(
364-
where={"user_id": end_user_id}, include={"litellm_budget_table": True}
365-
)
344+
if prisma_client is None:
345+
raise HTTPException(
346+
status_code=500,
347+
detail={"error": CommonProxyErrors.db_not_connected_error.value},
348+
)
366349

367-
if user_info is None:
368-
raise HTTPException(
369-
status_code=400,
370-
detail={"error": "End User Id={} does not exist in db".format(end_user_id)},
350+
user_info = await prisma_client.db.litellm_endusertable.find_first(
351+
where={"user_id": end_user_id}, include={"litellm_budget_table": True}
371352
)
372-
return user_info.model_dump(exclude_none=True)
373353

354+
if user_info is None:
355+
raise ProxyException(
356+
message="End User Id={} does not exist in db".format(end_user_id),
357+
type="not_found",
358+
code=404,
359+
param="end_user_id",
360+
)
361+
return user_info.model_dump(exclude_none=True)
362+
363+
except Exception as e:
364+
verbose_proxy_logger.exception(
365+
"litellm.proxy.management_endpoints.customer_endpoints.end_user_info(): Exception occured - {}".format(
366+
str(e)
367+
)
368+
)
369+
raise handle_exception_on_proxy(e)
374370

375371
@router.post(
376372
"/customer/update",
@@ -441,11 +437,11 @@ async def update_end_user(
441437
)
442438

443439
if end_user_table_data is None:
444-
raise HTTPException(
445-
status_code=400,
446-
detail={
447-
"error": "End User Id={} does not exist in db".format(data.user_id)
448-
},
440+
raise ProxyException(
441+
message="End User Id={} does not exist in db".format(data.user_id),
442+
type="not_found",
443+
code=404,
444+
param="user_id",
449445
)
450446

451447
end_user_table_data_typed = LiteLLM_EndUserTable(
@@ -524,22 +520,7 @@ async def update_end_user(
524520
str(e)
525521
)
526522
)
527-
if isinstance(e, HTTPException):
528-
raise ProxyException(
529-
message=getattr(e, "detail", f"Internal Server Error({str(e)})"),
530-
type="internal_error",
531-
param=getattr(e, "param", "None"),
532-
code=getattr(e, "status_code", status.HTTP_500_INTERNAL_SERVER_ERROR),
533-
)
534-
elif isinstance(e, ProxyException):
535-
raise e
536-
raise ProxyException(
537-
message="Internal Server Error, " + str(e),
538-
type="internal_error",
539-
param=getattr(e, "param", "None"),
540-
code=status.HTTP_500_INTERNAL_SERVER_ERROR,
541-
)
542-
pass
523+
raise handle_exception_on_proxy(e)
543524

544525

545526
@router.post(
@@ -587,17 +568,29 @@ async def delete_end_user(
587568
and isinstance(data.user_ids, list)
588569
and len(data.user_ids) > 0
589570
):
590-
response = await prisma_client.db.litellm_endusertable.delete_many(
571+
# First check if all users exist
572+
existing_users = await prisma_client.db.litellm_endusertable.find_many(
591573
where={"user_id": {"in": data.user_ids}}
592574
)
593-
if response is None:
594-
raise ValueError(
595-
f"Failed deleting customer data. User ID does not exist passed user_id={data.user_ids}"
596-
)
597-
if response != len(data.user_ids):
598-
raise ValueError(
599-
f"Failed deleting all customer data. User ID does not exist passed user_id={data.user_ids}. Deleted {response} customers, passed {len(data.user_ids)} customers"
575+
existing_user_ids = {user.user_id for user in existing_users}
576+
missing_user_ids = [
577+
user_id for user_id in data.user_ids if user_id not in existing_user_ids
578+
]
579+
580+
if missing_user_ids:
581+
raise ProxyException(
582+
message="End User Id(s)={} do not exist in db".format(
583+
", ".join(missing_user_ids)
584+
),
585+
type="not_found",
586+
code=404,
587+
param="user_ids",
600588
)
589+
590+
# All users exist, proceed with deletion
591+
response = await prisma_client.db.litellm_endusertable.delete_many(
592+
where={"user_id": {"in": data.user_ids}}
593+
)
601594
verbose_proxy_logger.debug(
602595
f"received response from updating prisma client. response={response}"
603596
)
@@ -616,24 +609,7 @@ async def delete_end_user(
616609
str(e)
617610
)
618611
)
619-
verbose_proxy_logger.debug(traceback.format_exc())
620-
if isinstance(e, HTTPException):
621-
raise ProxyException(
622-
message=getattr(e, "detail", f"Internal Server Error({str(e)})"),
623-
type="internal_error",
624-
param=getattr(e, "param", "None"),
625-
code=getattr(e, "status_code", status.HTTP_500_INTERNAL_SERVER_ERROR),
626-
)
627-
elif isinstance(e, ProxyException):
628-
raise e
629-
raise ProxyException(
630-
message="Internal Server Error, " + str(e),
631-
type="internal_error",
632-
param=getattr(e, "param", "None"),
633-
code=status.HTTP_500_INTERNAL_SERVER_ERROR,
634-
)
635-
pass
636-
612+
raise handle_exception_on_proxy(e)
637613

638614
@router.get(
639615
"/customer/list",
@@ -661,32 +637,41 @@ async def list_end_user(
661637
```
662638
663639
"""
664-
from litellm.proxy.proxy_server import prisma_client
640+
try:
641+
from litellm.proxy.proxy_server import prisma_client
665642

666-
if (
667-
user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN
668-
and user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY
669-
):
670-
raise HTTPException(
671-
status_code=401,
672-
detail={
673-
"error": "Admin-only endpoint. Your user role={}".format(
674-
user_api_key_dict.user_role
675-
)
676-
},
677-
)
643+
if (
644+
user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN
645+
and user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY
646+
):
647+
raise HTTPException(
648+
status_code=401,
649+
detail={
650+
"error": "Admin-only endpoint. Your user role={}".format(
651+
user_api_key_dict.user_role
652+
)
653+
},
654+
)
678655

679-
if prisma_client is None:
680-
raise HTTPException(
681-
status_code=400,
682-
detail={"error": CommonProxyErrors.db_not_connected_error.value},
683-
)
656+
if prisma_client is None:
657+
raise HTTPException(
658+
status_code=400,
659+
detail={"error": CommonProxyErrors.db_not_connected_error.value},
660+
)
684661

685-
response = await prisma_client.db.litellm_endusertable.find_many(
686-
include={"litellm_budget_table": True}
687-
)
662+
response = await prisma_client.db.litellm_endusertable.find_many(
663+
include={"litellm_budget_table": True}
664+
)
688665

689-
returned_response: List[LiteLLM_EndUserTable] = []
690-
for item in response:
691-
returned_response.append(LiteLLM_EndUserTable(**item.model_dump()))
692-
return returned_response
666+
returned_response: List[LiteLLM_EndUserTable] = []
667+
for item in response:
668+
returned_response.append(LiteLLM_EndUserTable(**item.model_dump()))
669+
return returned_response
670+
671+
except Exception as e:
672+
verbose_proxy_logger.exception(
673+
"litellm.proxy.management_endpoints.customer_endpoints.list_end_user(): Exception occured - {}".format(
674+
str(e)
675+
)
676+
)
677+
raise handle_exception_on_proxy(e)

0 commit comments

Comments
 (0)