From 359edbb1f7564f773e16d3691c7b2a2a428e11cd Mon Sep 17 00:00:00 2001 From: Cassandra Coyle Date: Thu, 12 Mar 2026 17:25:07 -0500 Subject: [PATCH 1/2] no 60s timeout by default Signed-off-by: Cassandra Coyle --- dapr/aio/clients/grpc/interceptors.py | 9 ++++++--- dapr/clients/grpc/interceptors.py | 9 ++++++--- dapr/conf/global_settings.py | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/dapr/aio/clients/grpc/interceptors.py b/dapr/aio/clients/grpc/interceptors.py index 0444d5acb..c97a9d10f 100644 --- a/dapr/aio/clients/grpc/interceptors.py +++ b/dapr/aio/clients/grpc/interceptors.py @@ -41,11 +41,14 @@ class _ClientCallDetailsAsync( class DaprClientTimeoutInterceptorAsync(UnaryUnaryClientInterceptor): def intercept_unary_unary(self, continuation, client_call_details, request): - # If a specific timeout is not set, create a new ClientCallDetails with the default timeout - if client_call_details.timeout is None: + # Only apply a deadline when DAPR_API_TIMEOUT_SECONDS is explicitly configured. + # Without an explicit setting there is no SDK-level default deadline: Dapr's own + # resiliency policies and component timeouts act as the authoritative bounds for + # long-running operations such as LLM calls and workflow activities. + if settings.DAPR_API_TIMEOUT_SECONDS is not None and client_call_details.timeout is None: new_client_call_details = _ClientCallDetailsAsync( client_call_details.method, - settings.DAPR_API_TIMEOUT_SECONDS, + float(settings.DAPR_API_TIMEOUT_SECONDS), client_call_details.metadata, client_call_details.credentials, client_call_details.wait_for_ready, diff --git a/dapr/clients/grpc/interceptors.py b/dapr/clients/grpc/interceptors.py index a574fb8c6..24df379a2 100644 --- a/dapr/clients/grpc/interceptors.py +++ b/dapr/clients/grpc/interceptors.py @@ -27,11 +27,14 @@ class _ClientCallDetails( class DaprClientTimeoutInterceptor(UnaryUnaryClientInterceptor): def intercept_unary_unary(self, continuation, client_call_details, request): - # If a specific timeout is not set, create a new ClientCallDetails with the default timeout - if client_call_details.timeout is None: + # Only apply a deadline when DAPR_API_TIMEOUT_SECONDS is explicitly configured. + # Without an explicit setting there is no SDK-level default deadline: Dapr's own + # resiliency policies and component timeouts act as the authoritative bounds for + # long-running operations such as LLM calls and workflow activities. + if settings.DAPR_API_TIMEOUT_SECONDS is not None and client_call_details.timeout is None: new_client_call_details = _ClientCallDetails( client_call_details.method, - settings.DAPR_API_TIMEOUT_SECONDS, + float(settings.DAPR_API_TIMEOUT_SECONDS), client_call_details.metadata, client_call_details.credentials, client_call_details.wait_for_ready, diff --git a/dapr/conf/global_settings.py b/dapr/conf/global_settings.py index 5a64e5d4c..dfce0d7c9 100644 --- a/dapr/conf/global_settings.py +++ b/dapr/conf/global_settings.py @@ -28,7 +28,7 @@ DAPR_HEALTH_TIMEOUT = 60 # seconds DAPR_API_MAX_RETRIES = 0 -DAPR_API_TIMEOUT_SECONDS = 60 +DAPR_API_TIMEOUT_SECONDS = None # No default deadline — set DAPR_API_TIMEOUT_SECONDS env var to impose one DAPR_API_METHOD_INVOCATION_PROTOCOL = 'http' From f76589ca5d82bf411292e82ee4ec123f6ac6d614 Mon Sep 17 00:00:00 2001 From: Cassandra Coyle Date: Mon, 16 Mar 2026 11:52:16 -0500 Subject: [PATCH 2/2] add test and fix lint Signed-off-by: Cassandra Coyle --- dapr/conf/global_settings.py | 4 +++- tests/clients/test_timeout_interceptor.py | 17 +++++++++++++++++ tests/clients/test_timeout_interceptor_async.py | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/dapr/conf/global_settings.py b/dapr/conf/global_settings.py index dfce0d7c9..5179f6a3a 100644 --- a/dapr/conf/global_settings.py +++ b/dapr/conf/global_settings.py @@ -28,7 +28,9 @@ DAPR_HEALTH_TIMEOUT = 60 # seconds DAPR_API_MAX_RETRIES = 0 -DAPR_API_TIMEOUT_SECONDS = None # No default deadline — set DAPR_API_TIMEOUT_SECONDS env var to impose one +DAPR_API_TIMEOUT_SECONDS = ( + None # No default deadline — set DAPR_API_TIMEOUT_SECONDS env var to impose one +) DAPR_API_METHOD_INVOCATION_PROTOCOL = 'http' diff --git a/tests/clients/test_timeout_interceptor.py b/tests/clients/test_timeout_interceptor.py index c60331bed..bddec8da7 100644 --- a/tests/clients/test_timeout_interceptor.py +++ b/tests/clients/test_timeout_interceptor.py @@ -54,3 +54,20 @@ def test_intercept_unary_unary_without_timeout(self): ) called_client_call_details = continuation.call_args[0][0] self.assertEqual(7, called_client_call_details.timeout) + + @patch.object(settings, 'DAPR_API_TIMEOUT_SECONDS', None) + def test_intercept_unary_unary_no_global_timeout_no_per_call_timeout(self): + continuation = Mock() + request = Mock() + client_call_details = Mock() + client_call_details.method = 'method' + client_call_details.timeout = None + client_call_details.metadata = 'metadata' + client_call_details.credentials = 'credentials' + client_call_details.wait_for_ready = 'wait_for_ready' + client_call_details.compression = 'compression' + + DaprClientTimeoutInterceptor().intercept_unary_unary( + continuation, client_call_details, request + ) + continuation.assert_called_once_with(client_call_details, request) diff --git a/tests/clients/test_timeout_interceptor_async.py b/tests/clients/test_timeout_interceptor_async.py index 88b5831dc..0e0651f12 100644 --- a/tests/clients/test_timeout_interceptor_async.py +++ b/tests/clients/test_timeout_interceptor_async.py @@ -52,3 +52,19 @@ def test_intercept_unary_unary_without_timeout(self): ) called_client_call_details = continuation.call_args[0][0] self.assertEqual(7, called_client_call_details.timeout) + + @patch.object(settings, 'DAPR_API_TIMEOUT_SECONDS', None) + def test_intercept_unary_unary_no_global_timeout_no_per_call_timeout(self): + continuation = Mock() + request = Mock() + client_call_details = Mock() + client_call_details.method = 'method' + client_call_details.timeout = None + client_call_details.metadata = 'metadata' + client_call_details.credentials = 'credentials' + client_call_details.wait_for_ready = 'wait_for_ready' + + DaprClientTimeoutInterceptorAsync().intercept_unary_unary( + continuation, client_call_details, request + ) + continuation.assert_called_once_with(client_call_details, request)