From 5e54ac6335a65a0e2dc1e3cd1a11c552d538d617 Mon Sep 17 00:00:00 2001 From: "Chris West (Faux)" Date: Fri, 20 Mar 2026 15:40:13 +0000 Subject: [PATCH] fix: async task leak in SlackAlerting add_deployment (from scheduler) -> _update_general_settings calls this function every 30s. Each call starts a new while True: loop. /debug/asyncio-tasks tells me there's thousands of active tasks with no names, which impacts my ability to work out why the application is hung. I don't think it's a big problem, but it's not helping with debuggability. --- .../integrations/SlackAlerting/slack_alerting.py | 5 +++-- .../SlackAlerting/test_slack_alerting.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/litellm/integrations/SlackAlerting/slack_alerting.py b/litellm/integrations/SlackAlerting/slack_alerting.py index 013cef74805..34a53a8be33 100644 --- a/litellm/integrations/SlackAlerting/slack_alerting.py +++ b/litellm/integrations/SlackAlerting/slack_alerting.py @@ -115,8 +115,9 @@ class SlackAlerting(CustomBatchLogger): ): if alerting is not None: self.alerting = alerting - asyncio.create_task(self.periodic_flush()) - self.periodic_started = True + if not self.periodic_started: + asyncio.create_task(self.periodic_flush()) + self.periodic_started = True if alerting_threshold is not None: self.alerting_threshold = alerting_threshold if alert_types is not None: diff --git a/tests/test_litellm/integrations/SlackAlerting/test_slack_alerting.py b/tests/test_litellm/integrations/SlackAlerting/test_slack_alerting.py index 128a88a0f12..8d8f88a30af 100644 --- a/tests/test_litellm/integrations/SlackAlerting/test_slack_alerting.py +++ b/tests/test_litellm/integrations/SlackAlerting/test_slack_alerting.py @@ -173,6 +173,21 @@ class TestSlackAlerting(unittest.TestCase): self.slack_alerting.update_values(alerting_args={"slack_alerting": "True"}) assert self.slack_alerting.periodic_started == True + # Repeated calls to update_values should not spawn additional periodic_flush tasks + @patch("asyncio.create_task") + def test_update_values_does_not_leak_periodic_tasks(self, mock_create_task): + mock_create_task.return_value = AsyncMock() + + self.slack_alerting.update_values(alerting=["slack"]) + assert self.slack_alerting.periodic_started == True + assert mock_create_task.call_count == 1 + + # Simulate repeated calls from add_deployment scheduler (runs every 30s) + self.slack_alerting.update_values(alerting=["slack"]) + self.slack_alerting.update_values(alerting=["slack"]) + self.slack_alerting.update_values(alerting_args={"slack_alerting": "True"}) + assert mock_create_task.call_count == 1 + @patch("litellm.integrations.SlackAlerting.slack_alerting.datetime") def test_alert_type_in_formatted_message(self, mock_datetime): # Setup mocks