From 5998fd124f64ac48f3d6ddc90e27894f2bd4f485 Mon Sep 17 00:00:00 2001 From: Sisyphus Date: Sun, 30 Aug 2026 16:20:35 +0800 Subject: [PATCH] fix(custom_httpx): report handler timeout in sync errors --- litellm/llms/custom_httpx/http_handler.py | 18 ++++++++++++------ .../llms/custom_httpx/test_http_handler.py | 10 ++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/litellm/llms/custom_httpx/http_handler.py b/litellm/llms/custom_httpx/http_handler.py index e2413469264..e346d0c1b5f 100644 --- a/litellm/llms/custom_httpx/http_handler.py +++ b/litellm/llms/custom_httpx/http_handler.py @@ -163,15 +163,21 @@ def _default_cached_client_timeout() -> httpx.Timeout: return httpx.Timeout(timeout=configured, connect=HTTP_HANDLER_CONNECT_TIMEOUT_SECONDS) -def _resolve_effective_timeout(timeout: float | httpx.Timeout | None) -> float | httpx.Timeout: +def _resolve_effective_timeout( + timeout: float | httpx.Timeout | None, + fallback_timeout: float | httpx.Timeout | None = None, +) -> float | httpx.Timeout: """Resolve the timeout actually in effect for a request. `None` means "use the client default", which `create_client` maps to `_DEFAULT_TIMEOUT`. An explicit `timeout=None` handed to httpx bypasses the client default entirely (no timeouts are enforced), so `None` must be resolved before building a request or rendering a Timeout error message. + `fallback_timeout` lets error messages preserve a handler-level timeout. """ - return timeout if timeout is not None else _DEFAULT_TIMEOUT + if timeout is not None: + return timeout + return fallback_timeout if fallback_timeout is not None else _DEFAULT_TIMEOUT _CLIENT_REFCOUNT_WHEN_HANDLER_IS_SOLE_REFERRER: Final = 2 @@ -1377,7 +1383,7 @@ class HTTPHandler: return response except httpx.TimeoutException: raise litellm.Timeout( - message=f"Connection timed out after {_resolve_effective_timeout(timeout)} seconds.", + message=f"Connection timed out after {_resolve_effective_timeout(timeout, self.timeout)} seconds.", model="default-model-name", llm_provider="litellm-httpx-handler", ) @@ -1427,7 +1433,7 @@ class HTTPHandler: return response except httpx.TimeoutException: raise litellm.Timeout( - message=f"Connection timed out after {_resolve_effective_timeout(timeout)} seconds.", + message=f"Connection timed out after {_resolve_effective_timeout(timeout, self.timeout)} seconds.", model="default-model-name", llm_provider="litellm-httpx-handler", ) @@ -1476,7 +1482,7 @@ class HTTPHandler: return response except httpx.TimeoutException: raise litellm.Timeout( - message=f"Connection timed out after {_resolve_effective_timeout(timeout)} seconds.", + message=f"Connection timed out after {_resolve_effective_timeout(timeout, self.timeout)} seconds.", model="default-model-name", llm_provider="litellm-httpx-handler", ) @@ -1526,7 +1532,7 @@ class HTTPHandler: return response except httpx.TimeoutException: raise litellm.Timeout( - message=f"Connection timed out after {_resolve_effective_timeout(timeout)} seconds.", + message=f"Connection timed out after {_resolve_effective_timeout(timeout, self.timeout)} seconds.", model="default-model-name", llm_provider="litellm-httpx-handler", ) diff --git a/tests/test_litellm/llms/custom_httpx/test_http_handler.py b/tests/test_litellm/llms/custom_httpx/test_http_handler.py index 3aa65a94ded..27bcebecabb 100644 --- a/tests/test_litellm/llms/custom_httpx/test_http_handler.py +++ b/tests/test_litellm/llms/custom_httpx/test_http_handler.py @@ -1603,6 +1603,16 @@ def test_sync_post_timeout_message_reports_client_default_when_timeout_unset(): assert f"{COMPLETION_HTTP_FALLBACK_SECONDS}" in str(exc_info.value) +def test_sync_post_timeout_message_reports_handler_timeout_when_request_timeout_unset(): + handler = HTTPHandler(timeout=7.25) + handler.client = _RecordingSyncClient() + + with pytest.raises(litellm.Timeout) as exc_info: + handler.post("https://example.test/v1/chat", json={"ping": True}) + + assert "7.25 seconds" in str(exc_info.value) + + def test_sync_post_timeout_message_reports_explicit_timeout(): handler = HTTPHandler() handler.client = _RecordingSyncClient()