From 5ad9af57d1602536af3092989dc3edefccb842f6 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Mon, 12 Oct 2026 09:42:26 +0200 Subject: [PATCH] fix: cannot connect through Redis Sentinels that require a password (#32241) When the Redis Sentinels are protected with a password, every Redis operation failed with "No master found", because the password in REDIS_URL was used for the Redis server itself but never for the Sentinels. The Sentinels now get the same credentials, both for REDIS_URL and for WEBSOCKET_REDIS_URL. Sentinels without a password keep working, and Sentinel credentials set by hand in WEBSOCKET_REDIS_OPTIONS still take precedence. Fixes #32240 --- backend/open_webui/socket/main.py | 11 ++++++++++- backend/open_webui/utils/redis.py | 10 ++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/socket/main.py b/backend/open_webui/socket/main.py index c2e7d810d5..5b2a826fee 100644 --- a/backend/open_webui/socket/main.py +++ b/backend/open_webui/socket/main.py @@ -58,6 +58,7 @@ from open_webui.utils.chat_id import is_saved_chat_id from open_webui.utils.json_codec import SOCKETIO_JSON, JSONCodec, dumps_bytes from open_webui.utils.misc import get_output_text from open_webui.utils.redis import ( + build_sentinel_kwargs, build_sentinel_url, get_redis_connection, get_sentinels_from_env, @@ -103,8 +104,16 @@ if WEBSOCKET_MANAGER == 'redis': if sentinel_hosts else WEBSOCKET_REDIS_URL ) + ws_redis_options = ( + { + 'sentinel_kwargs': build_sentinel_kwargs(WEBSOCKET_REDIS_URL, WEBSOCKET_REDIS_OPTIONS), + **WEBSOCKET_REDIS_OPTIONS, + } + if sentinel_hosts + else WEBSOCKET_REDIS_OPTIONS + ) manager_class = AsyncRedisRoomChannelManager if WEBSOCKET_REDIS_ROOM_CHANNELS else socketio.AsyncRedisManager - redis_manager = manager_class(ws_redis_url, redis_options=WEBSOCKET_REDIS_OPTIONS, json=SOCKETIO_JSON) + redis_manager = manager_class(ws_redis_url, redis_options=ws_redis_options, json=SOCKETIO_JSON) sio = socketio.AsyncServer( cors_allowed_origins=SOCKETIO_CORS_ORIGINS, async_mode='asgi', diff --git a/backend/open_webui/utils/redis.py b/backend/open_webui/utils/redis.py index 3bf8c36c02..a4f91fc619 100644 --- a/backend/open_webui/utils/redis.py +++ b/backend/open_webui/utils/redis.py @@ -86,6 +86,13 @@ def build_sentinel_url( return f'redis+sentinel://{auth}{nodes}/{cfg["db"]}/{cfg["service"]}' +def build_sentinel_kwargs(base_url: str, redis_options: dict[str, Any]) -> dict[str, Any]: + """Kwargs for the Sentinel connections: ``base_url`` credentials plus the ``socket_*`` options.""" + cfg = parse_redis_url(base_url) + socket_options = {key: value for key, value in redis_options.items() if key.startswith('socket_')} + return {'username': cfg['username'], 'password': cfg['password'], **socket_options} + + def get_redis_client(async_mode: bool = False) -> Any | None: """Create a Redis connection using settings from environment variables. @@ -283,6 +290,9 @@ def _build_sentinel( db=cfg['db'], username=cfg['username'], password=cfg['password'], + sentinel_kwargs=build_sentinel_kwargs( + url, {'socket_connect_timeout': REDIS_SOCKET_CONNECT_TIMEOUT, **_socket_options()} + ), decode_responses=decode_responses, socket_connect_timeout=REDIS_SOCKET_CONNECT_TIMEOUT, **{k: v for k, v in _socket_options().items() if k != 'socket_connect_timeout'},