From b90e9c95afd89e22c777f25971e65a8efab46895 Mon Sep 17 00:00:00 2001 From: Neev Modh <141932641+neevmodh@users.noreply.github.com> Date: Sat, 26 Sep 2026 14:03:59 +0530 Subject: [PATCH 1/2] fix(proxy): completion-marker ttl outliving hanging-request tracker entry update_request_status wrote the "request completed" marker with ttl = alerting_threshold + 100, while the hanging-request tracker entry (HangingRequestCheck) lives for alerting_threshold * 1.5 + HANGING_ALERT_BUFFER_TIME_SECONDS. Since the check loop only scans the 20 oldest tracked entries per pass, a sustained burst above ~8 req/min can let a completed request's tracker entry sit in the backlog long enough for its shorter-lived completion marker to expire first - the check then finds no marker, assumes the request is still running, and fires a false "hanging request" Slack alert for a request that already finished in under a second. Align the marker's ttl with the tracker entry's formula so a completed request's marker always outlives the window in which it might still be checked. Fixes #43285 --- litellm/proxy/utils.py | 15 ++++++++-- tests/test_litellm/proxy/test_proxy_utils.py | 29 ++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index b8cc30ad8a7..bed1b1e3e76 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -1434,9 +1434,18 @@ class ProxyLogging: # current alerting threshold alerting_threshold: float = self.alerting_threshold - # add a 100 second buffer to the alerting threshold - # ensures we don't send errant hanging request slack alerts - alerting_threshold += 100 + # This marker's ttl must outlive the hanging-request tracker entry + # (alerting_threshold * 1.5 + HANGING_ALERT_BUFFER_TIME_SECONDS, see + # HangingRequestCheck._get_metadata) - otherwise a completed request + # can have its marker expire before the tracker gets around to + # checking it (only the 20 oldest entries are scanned per pass), + # producing a false "hanging request" alert for a request that + # already finished successfully. + from litellm.types.integrations.slack_alerting import ( + HANGING_ALERT_BUFFER_TIME_SECONDS, + ) + + alerting_threshold = alerting_threshold * 1.5 + HANGING_ALERT_BUFFER_TIME_SECONDS await self.internal_usage_cache.async_set_cache( key=f"request_status:{litellm_call_id}", diff --git a/tests/test_litellm/proxy/test_proxy_utils.py b/tests/test_litellm/proxy/test_proxy_utils.py index ea1870d3b73..f2bc9f3c5a0 100644 --- a/tests/test_litellm/proxy/test_proxy_utils.py +++ b/tests/test_litellm/proxy/test_proxy_utils.py @@ -26,6 +26,35 @@ def test_get_custom_url(monkeypatch): assert custom_url == "http://0.0.0.0:4000/litellm/ui/" +@pytest.mark.asyncio +async def test_update_request_status_marker_ttl_outlives_tracker_entry(): + """The completion marker written by update_request_status must have a + ttl >= the hanging-request tracker entry's ttl (alerting_threshold * 1.5 + + HANGING_ALERT_BUFFER_TIME_SECONDS). Otherwise a completed request's + marker can expire before the hanging-request check gets around to + scanning it, producing a false "hanging request" alert. See #43285. + """ + from litellm.types.integrations.slack_alerting import ( + HANGING_ALERT_BUFFER_TIME_SECONDS, + ) + + proxy_logging_obj = ProxyLogging(user_api_key_cache=DualCache()) + proxy_logging_obj.alerting = ["slack"] + proxy_logging_obj.alerting_threshold = 300 + + tracker_entry_ttl = 300 * 1.5 + HANGING_ALERT_BUFFER_TIME_SECONDS + + with patch.object( + proxy_logging_obj.internal_usage_cache, "async_set_cache", new=AsyncMock() + ) as mock_set_cache: + await proxy_logging_obj.update_request_status( + litellm_call_id="test_call_id", status="success" + ) + + used_ttl = mock_set_cache.call_args.kwargs["ttl"] + assert used_ttl >= tracker_entry_ttl + + def test_proxy_only_error_true_for_llm_route(): proxy_logging_obj = ProxyLogging(user_api_key_cache=DualCache()) assert proxy_logging_obj._is_proxy_only_llm_api_error( From 943a967b55fdb50d84ff97cfb23a9c92a4e15c61 Mon Sep 17 00:00:00 2001 From: Neev Modh <141932641+neevmodh@users.noreply.github.com> Date: Sat, 26 Sep 2026 14:45:18 +0530 Subject: [PATCH 2/2] test(proxy): update stale marker-ttl expectation to match the fix --- .../test_litellm/proxy/utils/proxy_logging/test_lifecycle.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_lifecycle.py b/tests/test_litellm/proxy/utils/proxy_logging/test_lifecycle.py index 40cb3f10d34..4f6f8a3aeda 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_lifecycle.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_lifecycle.py @@ -483,7 +483,9 @@ async def test_update_request_status_when_alerting_set_writes_cache(proxy_loggin "key": "request_status:call-1", "value": "success", "local_only": True, - "ttl": 105.0, + # alerting_threshold * 1.5 + HANGING_ALERT_BUFFER_TIME_SECONDS, matching + # the hanging-request tracker entry's own ttl (see #43285). + "ttl": 67.5, }