diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 5462257c9b7..e88074f0568 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -4106,7 +4106,16 @@ class StandardLoggingPayloadSetup: from litellm.proxy.spend_tracking.cold_storage_handler import ColdStorageHandler # Only generate object key if cold storage is configured - configured_cold_storage_logger = ColdStorageHandler._get_configured_cold_storage_custom_logger() + try: + configured_cold_storage_logger = ( + ColdStorageHandler._get_configured_cold_storage_custom_logger() + ) + except Exception as e: + verbose_logger.debug( + f"Cold storage custom logger unavailable: {e}" + ) + return None + if configured_cold_storage_logger is None: return None diff --git a/litellm/proxy/spend_tracking/cold_storage_handler.py b/litellm/proxy/spend_tracking/cold_storage_handler.py index 21e785425ad..b3ab7b6224c 100644 --- a/litellm/proxy/spend_tracking/cold_storage_handler.py +++ b/litellm/proxy/spend_tracking/cold_storage_handler.py @@ -64,10 +64,31 @@ class ColdStorageHandler: @staticmethod def _get_configured_cold_storage_custom_logger() -> Optional[_custom_logger_compatible_callbacks_literal]: - from litellm.proxy.proxy_server import general_settings - cold_storage_custom_logger: Optional[str] = general_settings.get("cold_storage_custom_logger") - if not cold_storage_custom_logger: - verbose_proxy_logger.debug("No cold storage custom logger found in general settings") + """Return the configured cold storage custom logger. + + During interpreter shutdown importing ``proxy_server`` can raise a + ``RuntimeError`` (e.g. "can't register atexit after shutdown"). + In these scenarios we gracefully return ``None`` instead of bubbling + the exception up the call stack. + """ + + try: + from litellm.proxy.proxy_server import general_settings + except Exception as e: + verbose_proxy_logger.debug( + f"Unable to import proxy_server for cold storage logging: {e}" + ) return None - - return cast(_custom_logger_compatible_callbacks_literal, cold_storage_custom_logger) \ No newline at end of file + + cold_storage_custom_logger: Optional[str] = general_settings.get( + "cold_storage_custom_logger" + ) + if not cold_storage_custom_logger: + verbose_proxy_logger.debug( + "No cold storage custom logger found in general settings" + ) + return None + + return cast( + _custom_logger_compatible_callbacks_literal, cold_storage_custom_logger + ) \ No newline at end of file diff --git a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py index 317491d3172..84edbb3ac5c 100644 --- a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py +++ b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py @@ -464,3 +464,30 @@ async def test_e2e_generate_cold_storage_object_key_not_configured(): # Verify the result is None when cold storage is not configured assert result is None + + +@pytest.mark.asyncio +async def test_e2e_generate_cold_storage_object_key_runtime_error_handled(): + """Ensure runtime errors while loading cold storage logger are ignored.""" + from datetime import datetime, timezone + from unittest.mock import patch + + from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup + + start_time = datetime(2025, 1, 15, 10, 30, 45, 123456, timezone.utc) + response_id = "chatcmpl-test-runtime" + team_alias = "team" + + with patch( + "litellm.proxy.spend_tracking.cold_storage_handler.ColdStorageHandler._get_configured_cold_storage_custom_logger", + side_effect=RuntimeError("can't register atexit after shutdown"), + ): + result = StandardLoggingPayloadSetup._generate_cold_storage_object_key( + start_time=start_time, + response_id=response_id, + team_alias=team_alias, + ) + + # When an exception occurs retrieving the cold storage logger, the + # function should return None instead of raising. + assert result is None