fix(proxy): keep the database error when the log_db_metrics failure hook raises (#44383)

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
devin-ai-integration[bot] 2026-10-03 16:38:15 +00:00 • committed by GitHub
parent 4c6c84afb7
commit a5e9f275ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 40 additions and 10 deletions

View file

@ -13,6 +13,7 @@ from functools import wraps
from types import MappingProxyType
from typing import Final
from litellm._logging import verbose_proxy_logger
from litellm._service_logger import ServiceTypes
_PRISMA_CLIENT_CRUD: Final = frozenset({"get_data", "update_data", "delete_data"})
@ -200,14 +201,17 @@ async def _handle_logging_db_exception(
if _is_exception_related_to_db(e) is not True:
return False
await proxy_logging_obj.service_logging_obj.async_service_failure_hook(
error=e,
service=ServiceTypes.DB,
call_type=func.__name__,
parent_otel_span=kwargs.get("parent_otel_span"),
duration=(end_time - start_time).total_seconds(),
start_time=start_time,
end_time=end_time,
event_metadata=metadata_of(kwargs),
)
try:
await proxy_logging_obj.service_logging_obj.async_service_failure_hook(
error=e,
service=ServiceTypes.DB,
call_type=func.__name__,
parent_otel_span=kwargs.get("parent_otel_span"),
duration=(end_time - start_time).total_seconds(),
start_time=start_time,
end_time=end_time,
event_metadata=metadata_of(kwargs),
)
except Exception as hook_error:
verbose_proxy_logger.debug("log_db_metrics: failure hook raised for %s: %s", func.__name__, hook_error)
return True

View file

@ -5,6 +5,7 @@ from typing import Final
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from prisma.errors import PrismaError
from litellm._service_logger import ServiceTypes
from litellm.proxy.db.db_lookup_gate import bounded_db_lookup
@ -27,6 +28,16 @@ def success_hook() -> Iterator[AsyncMock]:
yield hook
@pytest.fixture
def failure_hook() -> Iterator[AsyncMock]:
hook: Final = AsyncMock()
with patch(
"litellm.proxy.proxy_server.proxy_logging_obj",
MagicMock(service_logging_obj=MagicMock(async_service_failure_hook=hook)),
):
yield hook
async def _db_call_types(hook: AsyncMock) -> tuple[str, ...]:
await asyncio.sleep(0)
return tuple(call.kwargs["call_type"] for call in hook.await_args_list if call.kwargs["service"] == ServiceTypes.DB)
@ -248,3 +259,18 @@ async def test_a_task_spawned_by_a_decorated_call_that_queries_after_it_returned
await background
assert await _db_call_types(success_hook) == ("read_key_row", "find_unique")
@pytest.mark.asyncio
async def test_a_raising_failure_hook_never_replaces_the_prisma_error(failure_hook: AsyncMock) -> None:
failure_hook.side_effect = RuntimeError("exporter down")
@log_db_metrics
async def insert_data(**kwargs: object) -> None:
raise PrismaError("connection reset")
with pytest.raises(PrismaError, match="connection reset"):
await insert_data(table_name="key")
assert failure_hook.await_count == 1
assert failure_hook.await_args_list[0].kwargs["call_type"] == "insert_data"