From 5219f85bfa39cfbe90c0c4e88e4d124cd26d9572 Mon Sep 17 00:00:00 2001 From: olyashok <50547198+olyashok@users.noreply.github.com> Date: Thu, 16 Oct 2025 12:49:50 -0400 Subject: [PATCH] feat: make websocket client wait timeout configurable (#11405) Co-authored-by: Alex Co-authored-by: Graham Neubig --- openhands/core/config/openhands_config.py | 4 ++++ openhands/server/session/session.py | 24 +++++++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/openhands/core/config/openhands_config.py b/openhands/core/config/openhands_config.py index d2905832be..037cc7f3d3 100644 --- a/openhands/core/config/openhands_config.py +++ b/openhands/core/config/openhands_config.py @@ -112,6 +112,10 @@ class OpenHandsConfig(BaseModel): max_concurrent_conversations: int = Field( default=3 ) # Maximum number of concurrent agent loops allowed per user + client_wait_timeout: int = Field( + default=30, + description='Timeout in seconds for waiting for websocket client connection during initialization', + ) mcp_host: str = Field(default=f'localhost:{os.getenv("port", 3000)}') mcp: MCPConfig = Field(default_factory=MCPConfig) kubernetes: KubernetesConfig = Field(default_factory=KubernetesConfig) diff --git a/openhands/server/session/session.py b/openhands/server/session/session.py index 46638e15b7..9c92838201 100644 --- a/openhands/server/session/session.py +++ b/openhands/server/session/session.py @@ -390,9 +390,15 @@ class WebSession: _waiting_times = 1 if self.sio: + # Get timeout from configuration, default to 30 seconds + client_wait_timeout = self.config.client_wait_timeout + self.logger.debug( + f'Using client wait timeout: {client_wait_timeout}s for session {self.sid}' + ) + # Wait once during initialization to avoid event push failures during websocket connection intervals while self._wait_websocket_initial_complete and ( - time.time() - _start_time < 2 + time.time() - _start_time < client_wait_timeout ): if bool( self.sio.manager.rooms.get('/', {}).get( @@ -400,12 +406,18 @@ class WebSession: ) ): break - self.logger.warning( - f'There is no listening client in the current room,' - f' waiting for the {_waiting_times}th attempt: {self.sid}' - ) + + # Progressive backoff: start with 0.1s, increase to 1s after 10 attempts + sleep_duration = 0.1 if _waiting_times <= 10 else 1.0 + + # Log every 2 seconds to reduce spam + if _waiting_times % (20 if sleep_duration == 0.1 else 2) == 0: + self.logger.debug( + f'There is no listening client in the current room,' + f' waiting for the {_waiting_times}th attempt (timeout: {client_wait_timeout}s): {self.sid}' + ) _waiting_times += 1 - await asyncio.sleep(0.1) + await asyncio.sleep(sleep_duration) self._wait_websocket_initial_complete = False await self.sio.emit('oh_event', data, to=ROOM_KEY.format(sid=self.sid))