[GH-ISSUE #20142] issue: YDocManager's remove_user_from_all_documents throws on websocket disconnect (with Redis Sentinel) #19099

Closed
opened 2026-04-20 01:25:16 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @Ithanil on GitHub (Dec 23, 2025).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/20142

Check Existing Issues

  • I have searched for any existing and/or related issues.
  • I have searched for any existing and/or related discussions.
  • I have also searched in the CLOSED issues AND CLOSED discussions and found no related items (your issue might already be addressed on the development branch!).
  • I am using the latest version of Open WebUI.

Installation Method

Git Clone

Open WebUI Version

0.6.43

Ollama Version (if applicable)

No response

Operating System

Debian 13

Browser (if applicable)

No response

Confirmation

  • I have read and followed all instructions in README.md.
  • I am using the latest version of both Open WebUI and Ollama.
  • I have included the browser console logs.
  • I have included the Docker container logs.
  • I have provided every relevant configuration, setting, and environment variable used in my setup.
  • I have clearly listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc).
  • I have documented step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation. My steps:
  • Start with the initial platform/version/OS and dependencies used,
  • Specify exact install/launch/configure commands,
  • List URLs visited, user input (incl. example values/emails/passwords if needed),
  • Describe all options and toggles enabled or changed,
  • Include any files or environmental changes,
  • Identify the expected and actual result at each stage,
  • Ensure any reasonably skilled user can follow and hit the same issue.

Expected Behavior

No error logs appearing when client's websocket is closed.

Actual Behavior

Error logs appear when client's websocket is closed (and session likely remains in SESSION_POOL).

Steps to Reproduce

  1. Use a Redis Sentinel setup
  2. Login with a browser, opening new chat
  3. Close tab
  4. Observe error in logs

Logs & Screenshots

/app/backend/open_webui/socket/utils.py:194: RuntimeWarning: coroutine 'SentinelRedisProxy.__getattr__.<locals>._wrapped' was never awaited
  async for key in self._redis.scan_iter(
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
disconnect async handler error
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 643, in _trigger_event
    ret = await handler(*args)
                ^^^^^^^^^^^^^^
TypeError: disconnect() takes 1 positional argument but 2 were given

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 509, in run_async_handler
    return await self.handlers[event](*args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 712, in _handle_eio_disconnect
    await self._handle_disconnect(eio_sid, n, reason)
  File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 589, in _handle_disconnect
    await self._trigger_event('disconnect', namespace, sid,
  File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 647, in _trigger_event
    ret = await handler(*args[:-1])
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/backend/open_webui/socket/main.py", line 687, in disconnect
    await YDOC_MANAGER.remove_user_from_all_documents(sid)
  File "/app/backend/open_webui/socket/utils.py", line 194, in remove_user_from_all_documents
    async for key in self._redis.scan_iter(
TypeError: 'async for' requires an object with __aiter__ method, got coroutine

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 515, in run_async_handler
    return await self.handlers[event](args[0])
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: AsyncServer._handle_eio_disconnect() missing 1 required positional argument: 'reason'
2025-12-23 16:33:58.287 | ERROR    | engineio.async_server:run_async_handler:521 - disconnect async handler error
Traceback (most recent call last):

  File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 643, in _trigger_event
    ret = await handler(*args)
                │        └ ('pgR9DQCcMLwRXXpTAAAB', 'transport close')
                └ <function disconnect at 0x7f03d802a980>

TypeError: disconnect() takes 1 positional argument but 2 were given


During handling of the above exception, another exception occurred:


Traceback (most recent call last):

  File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 509, in run_async_handler
    return await self.handlers[event](*args)
                 │    │        │       └ ('_-X0oizlPtoY3yy7AAAA', 'transport close')
                 │    │        └ 'disconnect'
                 │    └ {'connect': <bound method AsyncServer._handle_eio_connect of <socketio.async_server.AsyncServer object at 0x7f03d8202390>>, '...
                 └ <engineio.async_server.AsyncServer object at 0x7f03d821ee10>
  File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 712, in _handle_eio_disconnect
    await self._handle_disconnect(eio_sid, n, reason)
          │    │                  │        │  └ 'transport close'
          │    │                  │        └ '/'
          │    │                  └ '_-X0oizlPtoY3yy7AAAA'
          │    └ <function AsyncServer._handle_disconnect at 0x7f03d7fa5120>
          └ <socketio.async_server.AsyncServer object at 0x7f03d8202390>
  File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 589, in _handle_disconnect
    await self._trigger_event('disconnect', namespace, sid,
          │    │                            │          └ 'pgR9DQCcMLwRXXpTAAAB'
          │    │                            └ '/'
          │    └ <function AsyncServer._trigger_event at 0x7f03d7fa53a0>
          └ <socketio.async_server.AsyncServer object at 0x7f03d8202390>
  File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 647, in _trigger_event
    ret = await handler(*args[:-1])
                │        └ ('pgR9DQCcMLwRXXpTAAAB', 'transport close')
                └ <function disconnect at 0x7f03d802a980>

  File "/app/backend/open_webui/socket/main.py", line 687, in disconnect
    await YDOC_MANAGER.remove_user_from_all_documents(sid)
          │            │                              └ 'pgR9DQCcMLwRXXpTAAAB'
          │            └ <function YdocManager.remove_user_from_all_documents at 0x7f03d8029a80>
          └ <open_webui.socket.utils.YdocManager object at 0x7f03d7ca49d0>

  File "/app/backend/open_webui/socket/utils.py", line 194, in remove_user_from_all_documents
    async for key in self._redis.scan_iter(
                     │    └ <open_webui.utils.redis.SentinelRedisProxy object at 0x7f03d821ef50>
                     └ <open_webui.socket.utils.YdocManager object at 0x7f03d7ca49d0>

TypeError: 'async for' requires an object with __aiter__ method, got coroutine


During handling of the above exception, another exception occurred:


Traceback (most recent call last):

  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/usr/local/lib/python3.11/site-packages/uvicorn/__main__.py", line 4, in <module>
    uvicorn.main()
    │       └ <Command main>
    └ <module 'uvicorn' from '/usr/local/lib/python3.11/site-packages/uvicorn/__init__.py'>
  File "/usr/local/lib/python3.11/site-packages/click/core.py", line 1485, in __call__
    return self.main(*args, **kwargs)
           │    │     │       └ {}
           │    │     └ ()
           │    └ <function Command.main at 0x7f0404f9ae80>
           └ <Command main>
  File "/usr/local/lib/python3.11/site-packages/click/core.py", line 1406, in main
    rv = self.invoke(ctx)
         │    │      └ <click.core.Context object at 0x7f0405cb9250>
         │    └ <function Command.invoke at 0x7f0404f9ab60>
         └ <Command main>
  File "/usr/local/lib/python3.11/site-packages/click/core.py", line 1269, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           │   │      │    │           │   └ {'host': '0.0.0.0', 'port': 8080, 'forwarded_allow_ips': '*', 'workers': 1, 'app': 'open_webui.main:app', 'uds': None, 'fd': ...
           │   │      │    │           └ <click.core.Context object at 0x7f0405cb9250>
           │   │      │    └ <function main at 0x7f0404c258a0>
           │   │      └ <Command main>
           │   └ <function Context.invoke at 0x7f0404f99da0>
           └ <click.core.Context object at 0x7f0405cb9250>
  File "/usr/local/lib/python3.11/site-packages/click/core.py", line 824, in invoke
    return callback(*args, **kwargs)
           │         │       └ {'host': '0.0.0.0', 'port': 8080, 'forwarded_allow_ips': '*', 'workers': 1, 'app': 'open_webui.main:app', 'uds': None, 'fd': ...
           │         └ ()
           └ <function main at 0x7f0404c258a0>
  File "/usr/local/lib/python3.11/site-packages/uvicorn/main.py", line 423, in main
    run(
    └ <function run at 0x7f0404ead6c0>
  File "/usr/local/lib/python3.11/site-packages/uvicorn/main.py", line 593, in run
    server.run()
    │      └ <function Server.run at 0x7f0404eacf40>
    └ <uvicorn.server.Server object at 0x7f0404e38750>
  File "/usr/local/lib/python3.11/site-packages/uvicorn/server.py", line 67, in run
    return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
           │           │    │             │                      │    │      └ <function Config.get_loop_factory at 0x7f0404e35580>
           │           │    │             │                      │    └ <uvicorn.config.Config object at 0x7f0404c29550>
           │           │    │             │                      └ <uvicorn.server.Server object at 0x7f0404e38750>
           │           │    │             └ None
           │           │    └ <function Server.serve at 0x7f0404eacfe0>
           │           └ <uvicorn.server.Server object at 0x7f0404e38750>
           └ <function asyncio_run at 0x7f0404eacea0>
  File "/usr/local/lib/python3.11/site-packages/uvicorn/_compat.py", line 23, in asyncio_run
    return runner.run(main)
           │      │   └ <coroutine object Server.serve at 0x7f0404bf97b0>
           │      └ <function Runner.run at 0x7f04051f4fe0>
           └ <asyncio.runners.Runner object at 0x7f0404f1f0d0>
  File "/usr/local/lib/python3.11/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           │    │     │                  └ <Task pending name='Task-1' coro=<Server.serve() running at /usr/local/lib/python3.11/site-packages/uvicorn/server.py:71> wai...
           │    │     └ <cyfunction Loop.run_until_complete at 0x7f0404c70580>
           │    └ <uvloop.Loop running=True closed=False debug=False>
           └ <asyncio.runners.Runner object at 0x7f0404f1f0d0>
  File "/usr/local/lib/python3.11/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 244, in run_asgi
    result = await self.app(self.scope, self.asgi_receive, self.asgi_send)  # type: ignore[func-returns-value]
                   │    │   │    │      │    │             │    └ <function WebSocketProtocol.asgi_send at 0x7f04046a1120>
                   │    │   │    │      │    │             └ <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03a41fc2d0>
                   │    │   │    │      │    └ <function WebSocketProtocol.asgi_receive at 0x7f04046a11c0>
                   │    │   │    │      └ <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03a41fc2d0>
                   │    │   │    └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
                   │    │   └ <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03a41fc2d0>
                   │    └ <uvicorn.middleware.proxy_headers.ProxyHeadersMiddleware object at 0x7f03a93dfd90>
                   └ <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03a41fc2d0>
  File "/usr/local/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__
    return await self.app(scope, receive, send)
                 │    │   │      │        └ <bound method WebSocketProtocol.asgi_send of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03...
                 │    │   │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
                 │    │   └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
                 │    └ <fastapi.applications.FastAPI object at 0x7f03a9656ad0>
                 └ <uvicorn.middleware.proxy_headers.ProxyHeadersMiddleware object at 0x7f03a93dfd90>
  File "/usr/local/lib/python3.11/site-packages/fastapi/applications.py", line 1135, in __call__
    await super().__call__(scope, receive, send)
                           │      │        └ <bound method WebSocketProtocol.asgi_send of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03...
                           │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
                           └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
  File "/usr/local/lib/python3.11/site-packages/starlette/applications.py", line 107, in __call__
    await self.middleware_stack(scope, receive, send)
          │    │                │      │        └ <bound method WebSocketProtocol.asgi_send of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03...
          │    │                │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │    │                └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          │    └ <starlette.middleware.errors.ServerErrorMiddleware object at 0x7f03a8f85d10>
          └ <fastapi.applications.FastAPI object at 0x7f03a9656ad0>
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 151, in __call__
    await self.app(scope, receive, send)
          │    │   │      │        └ <bound method WebSocketProtocol.asgi_send of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03...
          │    │   │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │    │   └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          │    └ <starlette.middleware.sessions.SessionMiddleware object at 0x7f03a91c9b50>
          └ <starlette.middleware.errors.ServerErrorMiddleware object at 0x7f03a8f85d10>
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/sessions.py", line 85, in __call__
    await self.app(scope, receive, send_wrapper)
          │    │   │      │        └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0>
          │    │   │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │    │   └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          │    └ <starlette.middleware.cors.CORSMiddleware object at 0x7f03a9316710>
          └ <starlette.middleware.sessions.SessionMiddleware object at 0x7f03a91c9b50>
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/cors.py", line 77, in __call__
    await self.app(scope, receive, send)
          │    │   │      │        └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0>
          │    │   │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │    │   └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          │    └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x7f03a9316310>
          └ <starlette.middleware.cors.CORSMiddleware object at 0x7f03a9316710>
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 103, in __call__
    await self.app(scope, receive, send)
          │    │   │      │        └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0>
          │    │   │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │    │   └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          │    └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x7f03a8fd0310>
          └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x7f03a9316310>
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 103, in __call__
    await self.app(scope, receive, send)
          │    │   │      │        └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0>
          │    │   │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │    │   └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          │    └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x7f03a96df950>
          └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x7f03a8fd0310>
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 103, in __call__
    await self.app(scope, receive, send)
          │    │   │      │        └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0>
          │    │   │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │    │   └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          │    └ <open_webui.main.APIKeyRestrictionMiddleware object at 0x7f03a92d1450>
          └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x7f03a96df950>
  [Previous line repeated 3 more times]
  File "/usr/local/lib/python3.11/site-packages/starlette_compress/__init__.py", line 86, in __call__
    return await self.app(scope, receive, send)
                 │    │   │      │        └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0>
                 │    │   │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
                 │    │   └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
                 │    └ <member 'app' of 'CompressMiddleware' objects>
                 └ <starlette_compress.CompressMiddleware object at 0x7f0404839f80>
  File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 63, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
          │                            │    │    │     │      │        └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0>
          │                            │    │    │     │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │                            │    │    │     └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          │                            │    │    └ <starlette.websockets.WebSocket object at 0x7f03a6811810>
          │                            │    └ <fastapi.middleware.asyncexitstack.AsyncExitStackMiddleware object at 0x7f03a9316cd0>
          │                            └ <starlette.middleware.exceptions.ExceptionMiddleware object at 0x7f03a9428090>
          └ <function wrap_app_handling_exceptions at 0x7f04022d3880>
  File "/usr/local/lib/python3.11/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
    await app(scope, receive, sender)
          │   │      │        └ <function wrap_app_handling_exceptions.<locals>.wrapped_app.<locals>.sender at 0x7f03a4217ec0>
          │   │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │   └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          └ <fastapi.middleware.asyncexitstack.AsyncExitStackMiddleware object at 0x7f03a9316cd0>
  File "/usr/local/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
    await self.app(scope, receive, send)
          │    │   │      │        └ <function wrap_app_handling_exceptions.<locals>.wrapped_app.<locals>.sender at 0x7f03a4217ec0>
          │    │   │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │    │   └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          │    └ <fastapi.routing.APIRouter object at 0x7f03a96fb710>
          └ <fastapi.middleware.asyncexitstack.AsyncExitStackMiddleware object at 0x7f03a9316cd0>
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 716, in __call__
    await self.middleware_stack(scope, receive, send)
          │    │                │      │        └ <function wrap_app_handling_exceptions.<locals>.wrapped_app.<locals>.sender at 0x7f03a4217ec0>
          │    │                │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │    │                └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          │    └ <bound method Router.app of <fastapi.routing.APIRouter object at 0x7f03a96fb710>>
          └ <fastapi.routing.APIRouter object at 0x7f03a96fb710>
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 736, in app
    await route.handle(scope, receive, send)
          │     │      │      │        └ <function wrap_app_handling_exceptions.<locals>.wrapped_app.<locals>.sender at 0x7f03a4217ec0>
          │     │      │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │     │      └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          │     └ <function Mount.handle at 0x7f04022f5800>
          └ Mount(path='/ws', name='', app=<socketio.asgi.ASGIApp object at 0x7f03d81fd210>)
  File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 462, in handle
    await self.app(scope, receive, send)
          │    │   │      │        └ <function wrap_app_handling_exceptions.<locals>.wrapped_app.<locals>.sender at 0x7f03a4217ec0>
          │    │   │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │    │   └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          │    └ <socketio.asgi.ASGIApp object at 0x7f03d81fd210>
          └ Mount(path='/ws', name='', app=<socketio.asgi.ASGIApp object at 0x7f03d81fd210>)
  File "/usr/local/lib/python3.11/site-packages/engineio/async_drivers/asgi.py", line 68, in __call__
    await self.engineio_server.handle_request(scope, receive, send)
          │    │               │              │      │        └ <function wrap_app_handling_exceptions.<locals>.wrapped_app.<locals>.sender at 0x7f03a4217ec0>
          │    │               │              │      └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7...
          │    │               │              └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('...
          │    │               └ <function AsyncServer.handle_request at 0x7f03d7fa4cc0>
          │    └ <socketio.async_server.AsyncServer object at 0x7f03d8202390>
          └ <socketio.asgi.ASGIApp object at 0x7f03d81fd210>
  File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 452, in handle_request
    return await self.eio.handle_request(*args, **kwargs)
                 │    │   │               │       └ {}
                 │    │   │               └ ({'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': (...
                 │    │   └ <function AsyncServer.handle_request at 0x7f03d80fd800>
                 │    └ <engineio.async_server.AsyncServer object at 0x7f03d821ee10>
                 └ <socketio.async_server.AsyncServer object at 0x7f03d8202390>
  File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 282, in handle_request
    r = await self._handle_connect(environ, transport,
              │    │               │        └ 'websocket'
              │    │               └ {'wsgi.input': <engineio.async_drivers.asgi.translate_request.<locals>.AwaitablePayload object at 0x7f03a68125d0>, 'wsgi.erro...
              │    └ <function AsyncServer._handle_connect at 0x7f03d80fdd00>
              └ <engineio.async_server.AsyncServer object at 0x7f03d821ee10>
  File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 473, in _handle_connect
    ret = await s.handle_get_request(environ)
                │ │                  └ {'wsgi.input': <engineio.async_drivers.asgi.translate_request.<locals>.AwaitablePayload object at 0x7f03a68125d0>, 'wsgi.erro...
                │ └ <function AsyncSocket.handle_get_request at 0x7f03d80fcfe0>
                └ <engineio.async_socket.AsyncSocket object at 0x7f03a8efb6d0>
  File "/usr/local/lib/python3.11/site-packages/engineio/async_socket.py", line 91, in handle_get_request
    return await getattr(self, '_upgrade_' + transport)(environ)
                         │                   │          └ {'wsgi.input': <engineio.async_drivers.asgi.translate_request.<locals>.AwaitablePayload object at 0x7f03a68125d0>, 'wsgi.erro...
                         │                   └ 'websocket'
                         └ <engineio.async_socket.AsyncSocket object at 0x7f03a8efb6d0>
  File "/usr/local/lib/python3.11/site-packages/engineio/async_socket.py", line 149, in _upgrade_websocket
    return await ws(environ)
                 │  └ {'wsgi.input': <engineio.async_drivers.asgi.translate_request.<locals>.AwaitablePayload object at 0x7f03a68125d0>, 'wsgi.erro...
                 └ <engineio.async_drivers.asgi.WebSocket object at 0x7f03a6832690>
  File "/usr/local/lib/python3.11/site-packages/engineio/async_drivers/asgi.py", line 258, in __call__
    await self.handler(self)
          │    │       └ <engineio.async_drivers.asgi.WebSocket object at 0x7f03a6832690>
          │    └ <bound method AsyncSocket._websocket_handler of <engineio.async_socket.AsyncSocket object at 0x7f03a8efb6d0>>
          └ <engineio.async_drivers.asgi.WebSocket object at 0x7f03a6832690>
  File "/usr/local/lib/python3.11/site-packages/engineio/async_socket.py", line 260, in _websocket_handler
    await self.close(wait=False, abort=True,
          │    └ <function AsyncSocket.close at 0x7f03d80fd120>
          └ <engineio.async_socket.AsyncSocket object at 0x7f03a8efb6d0>
  File "/usr/local/lib/python3.11/site-packages/engineio/async_socket.py", line 120, in close
    await self.server._trigger_event(
          │    │      └ <function AsyncServer._trigger_event at 0x7f03d80fdda0>
          │    └ <engineio.async_server.AsyncServer object at 0x7f03d821ee10>
          └ <engineio.async_socket.AsyncSocket object at 0x7f03a8efb6d0>
  File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 532, in _trigger_event
    ret = await run_async_handler()
                └ <function AsyncServer._trigger_event.<locals>.run_async_handler at 0x7f03a41e7ec0>
> File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 515, in run_async_handler
    return await self.handlers[event](args[0])
                 │    │        │      └ ('_-X0oizlPtoY3yy7AAAA', 'transport close')
                 │    │        └ 'disconnect'
                 │    └ {'connect': <bound method AsyncServer._handle_eio_connect of <socketio.async_server.AsyncServer object at 0x7f03d8202390>>, '...
                 └ <engineio.async_server.AsyncServer object at 0x7f03d821ee10>

TypeError: AsyncServer._handle_eio_disconnect() missing 1 required positional argument: 'reason'

Additional Information

I suspect the issue has been introduced by https://github.com/open-webui/open-webui/pull/19871 / 8e661a4e73 , because it does not happen with Open WebUI 0.6.40, which does not contain that commit yet and nothing else seems related.

Also, here is what GPT-5.2-Codex had to say when fed with the logs and repo as context:

Draft issue report (code‑backed, concise)

Title: Redis Sentinel: scan_iter wrapped as coroutine breaks async iteration; Socket.IO disconnect handler signature mismatch

Details

When WEBSOCKET_MANAGER == "redis", REDIS is created with get_redis_connection(..., async_mode=True) and passed into YdocManager. main.py (lines 105-162)

If Sentinel hosts are configured, get_redis_connection returns SentinelRedisProxy instead of a direct Redis client. redis.py (lines 153-173)

In async mode, SentinelRedisProxy.getattr wraps every method in an async def _wrapped and returns that wrapper. This makes self._redis.scan_iter(...) a coroutine. redis.py (lines 33-73)

YdocManager.remove_user_from_all_documents uses async for key in self._redis.scan_iter(...) without awaiting the coroutine. In Python, async for requires an object with aiter, so this will raise TypeError: 'async for' requires an object with aiter method, got coroutine and trigger the “coroutine was never awaited” warning. utils.py (lines 191-205)

The Socket.IO disconnect handler is defined as async def disconnect(sid) (single positional argument), but the provided log shows it is invoked with two positional arguments (sid, reason). That mismatch raises TypeError: disconnect() takes 1 positional argument but 2 were given. main.py (lines 682-688)

Originally created by @Ithanil on GitHub (Dec 23, 2025). Original GitHub issue: https://github.com/open-webui/open-webui/issues/20142 ### Check Existing Issues - [x] I have searched for any existing and/or related issues. - [x] I have searched for any existing and/or related discussions. - [x] I have also searched in the CLOSED issues AND CLOSED discussions and found no related items (your issue might already be addressed on the development branch!). - [x] I am using the latest version of Open WebUI. ### Installation Method Git Clone ### Open WebUI Version 0.6.43 ### Ollama Version (if applicable) _No response_ ### Operating System Debian 13 ### Browser (if applicable) _No response_ ### Confirmation - [x] I have read and followed all instructions in `README.md`. - [x] I am using the latest version of **both** Open WebUI and Ollama. - [x] I have included the browser console logs. - [x] I have included the Docker container logs. - [x] I have **provided every relevant configuration, setting, and environment variable used in my setup.** - [x] I have clearly **listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup** (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc). - [x] I have documented **step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation**. My steps: - Start with the initial platform/version/OS and dependencies used, - Specify exact install/launch/configure commands, - List URLs visited, user input (incl. example values/emails/passwords if needed), - Describe all options and toggles enabled or changed, - Include any files or environmental changes, - Identify the expected and actual result at each stage, - Ensure any reasonably skilled user can follow and hit the same issue. ### Expected Behavior No error logs appearing when client's websocket is closed. ### Actual Behavior Error logs appear when client's websocket is closed (and session likely remains in SESSION_POOL). ### Steps to Reproduce 1. Use a Redis Sentinel setup 2. Login with a browser, opening new chat 3. Close tab 4. Observe error in logs ### Logs & Screenshots ``` /app/backend/open_webui/socket/utils.py:194: RuntimeWarning: coroutine 'SentinelRedisProxy.__getattr__.<locals>._wrapped' was never awaited async for key in self._redis.scan_iter( RuntimeWarning: Enable tracemalloc to get the object allocation traceback disconnect async handler error Traceback (most recent call last): File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 643, in _trigger_event ret = await handler(*args) ^^^^^^^^^^^^^^ TypeError: disconnect() takes 1 positional argument but 2 were given During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 509, in run_async_handler return await self.handlers[event](*args) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 712, in _handle_eio_disconnect await self._handle_disconnect(eio_sid, n, reason) File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 589, in _handle_disconnect await self._trigger_event('disconnect', namespace, sid, File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 647, in _trigger_event ret = await handler(*args[:-1]) ^^^^^^^^^^^^^^^^^^^^^^^^^ File "/app/backend/open_webui/socket/main.py", line 687, in disconnect await YDOC_MANAGER.remove_user_from_all_documents(sid) File "/app/backend/open_webui/socket/utils.py", line 194, in remove_user_from_all_documents async for key in self._redis.scan_iter( TypeError: 'async for' requires an object with __aiter__ method, got coroutine During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 515, in run_async_handler return await self.handlers[event](args[0]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: AsyncServer._handle_eio_disconnect() missing 1 required positional argument: 'reason' 2025-12-23 16:33:58.287 | ERROR | engineio.async_server:run_async_handler:521 - disconnect async handler error Traceback (most recent call last): File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 643, in _trigger_event ret = await handler(*args) │ └ ('pgR9DQCcMLwRXXpTAAAB', 'transport close') └ <function disconnect at 0x7f03d802a980> TypeError: disconnect() takes 1 positional argument but 2 were given During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 509, in run_async_handler return await self.handlers[event](*args) │ │ │ └ ('_-X0oizlPtoY3yy7AAAA', 'transport close') │ │ └ 'disconnect' │ └ {'connect': <bound method AsyncServer._handle_eio_connect of <socketio.async_server.AsyncServer object at 0x7f03d8202390>>, '... └ <engineio.async_server.AsyncServer object at 0x7f03d821ee10> File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 712, in _handle_eio_disconnect await self._handle_disconnect(eio_sid, n, reason) │ │ │ │ └ 'transport close' │ │ │ └ '/' │ │ └ '_-X0oizlPtoY3yy7AAAA' │ └ <function AsyncServer._handle_disconnect at 0x7f03d7fa5120> └ <socketio.async_server.AsyncServer object at 0x7f03d8202390> File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 589, in _handle_disconnect await self._trigger_event('disconnect', namespace, sid, │ │ │ └ 'pgR9DQCcMLwRXXpTAAAB' │ │ └ '/' │ └ <function AsyncServer._trigger_event at 0x7f03d7fa53a0> └ <socketio.async_server.AsyncServer object at 0x7f03d8202390> File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 647, in _trigger_event ret = await handler(*args[:-1]) │ └ ('pgR9DQCcMLwRXXpTAAAB', 'transport close') └ <function disconnect at 0x7f03d802a980> File "/app/backend/open_webui/socket/main.py", line 687, in disconnect await YDOC_MANAGER.remove_user_from_all_documents(sid) │ │ └ 'pgR9DQCcMLwRXXpTAAAB' │ └ <function YdocManager.remove_user_from_all_documents at 0x7f03d8029a80> └ <open_webui.socket.utils.YdocManager object at 0x7f03d7ca49d0> File "/app/backend/open_webui/socket/utils.py", line 194, in remove_user_from_all_documents async for key in self._redis.scan_iter( │ └ <open_webui.utils.redis.SentinelRedisProxy object at 0x7f03d821ef50> └ <open_webui.socket.utils.YdocManager object at 0x7f03d7ca49d0> TypeError: 'async for' requires an object with __aiter__ method, got coroutine During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<frozen runpy>", line 198, in _run_module_as_main File "<frozen runpy>", line 88, in _run_code File "/usr/local/lib/python3.11/site-packages/uvicorn/__main__.py", line 4, in <module> uvicorn.main() │ └ <Command main> └ <module 'uvicorn' from '/usr/local/lib/python3.11/site-packages/uvicorn/__init__.py'> File "/usr/local/lib/python3.11/site-packages/click/core.py", line 1485, in __call__ return self.main(*args, **kwargs) │ │ │ └ {} │ │ └ () │ └ <function Command.main at 0x7f0404f9ae80> └ <Command main> File "/usr/local/lib/python3.11/site-packages/click/core.py", line 1406, in main rv = self.invoke(ctx) │ │ └ <click.core.Context object at 0x7f0405cb9250> │ └ <function Command.invoke at 0x7f0404f9ab60> └ <Command main> File "/usr/local/lib/python3.11/site-packages/click/core.py", line 1269, in invoke return ctx.invoke(self.callback, **ctx.params) │ │ │ │ │ └ {'host': '0.0.0.0', 'port': 8080, 'forwarded_allow_ips': '*', 'workers': 1, 'app': 'open_webui.main:app', 'uds': None, 'fd': ... │ │ │ │ └ <click.core.Context object at 0x7f0405cb9250> │ │ │ └ <function main at 0x7f0404c258a0> │ │ └ <Command main> │ └ <function Context.invoke at 0x7f0404f99da0> └ <click.core.Context object at 0x7f0405cb9250> File "/usr/local/lib/python3.11/site-packages/click/core.py", line 824, in invoke return callback(*args, **kwargs) │ │ └ {'host': '0.0.0.0', 'port': 8080, 'forwarded_allow_ips': '*', 'workers': 1, 'app': 'open_webui.main:app', 'uds': None, 'fd': ... │ └ () └ <function main at 0x7f0404c258a0> File "/usr/local/lib/python3.11/site-packages/uvicorn/main.py", line 423, in main run( └ <function run at 0x7f0404ead6c0> File "/usr/local/lib/python3.11/site-packages/uvicorn/main.py", line 593, in run server.run() │ └ <function Server.run at 0x7f0404eacf40> └ <uvicorn.server.Server object at 0x7f0404e38750> File "/usr/local/lib/python3.11/site-packages/uvicorn/server.py", line 67, in run return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory()) │ │ │ │ │ │ └ <function Config.get_loop_factory at 0x7f0404e35580> │ │ │ │ │ └ <uvicorn.config.Config object at 0x7f0404c29550> │ │ │ │ └ <uvicorn.server.Server object at 0x7f0404e38750> │ │ │ └ None │ │ └ <function Server.serve at 0x7f0404eacfe0> │ └ <uvicorn.server.Server object at 0x7f0404e38750> └ <function asyncio_run at 0x7f0404eacea0> File "/usr/local/lib/python3.11/site-packages/uvicorn/_compat.py", line 23, in asyncio_run return runner.run(main) │ │ └ <coroutine object Server.serve at 0x7f0404bf97b0> │ └ <function Runner.run at 0x7f04051f4fe0> └ <asyncio.runners.Runner object at 0x7f0404f1f0d0> File "/usr/local/lib/python3.11/asyncio/runners.py", line 118, in run return self._loop.run_until_complete(task) │ │ │ └ <Task pending name='Task-1' coro=<Server.serve() running at /usr/local/lib/python3.11/site-packages/uvicorn/server.py:71> wai... │ │ └ <cyfunction Loop.run_until_complete at 0x7f0404c70580> │ └ <uvloop.Loop running=True closed=False debug=False> └ <asyncio.runners.Runner object at 0x7f0404f1f0d0> File "/usr/local/lib/python3.11/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 244, in run_asgi result = await self.app(self.scope, self.asgi_receive, self.asgi_send) # type: ignore[func-returns-value] │ │ │ │ │ │ │ └ <function WebSocketProtocol.asgi_send at 0x7f04046a1120> │ │ │ │ │ │ └ <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03a41fc2d0> │ │ │ │ │ └ <function WebSocketProtocol.asgi_receive at 0x7f04046a11c0> │ │ │ │ └ <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03a41fc2d0> │ │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ │ └ <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03a41fc2d0> │ └ <uvicorn.middleware.proxy_headers.ProxyHeadersMiddleware object at 0x7f03a93dfd90> └ <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03a41fc2d0> File "/usr/local/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__ return await self.app(scope, receive, send) │ │ │ │ └ <bound method WebSocketProtocol.asgi_send of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03... │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ └ <fastapi.applications.FastAPI object at 0x7f03a9656ad0> └ <uvicorn.middleware.proxy_headers.ProxyHeadersMiddleware object at 0x7f03a93dfd90> File "/usr/local/lib/python3.11/site-packages/fastapi/applications.py", line 1135, in __call__ await super().__call__(scope, receive, send) │ │ └ <bound method WebSocketProtocol.asgi_send of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03... │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... File "/usr/local/lib/python3.11/site-packages/starlette/applications.py", line 107, in __call__ await self.middleware_stack(scope, receive, send) │ │ │ │ └ <bound method WebSocketProtocol.asgi_send of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03... │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ └ <starlette.middleware.errors.ServerErrorMiddleware object at 0x7f03a8f85d10> └ <fastapi.applications.FastAPI object at 0x7f03a9656ad0> File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 151, in __call__ await self.app(scope, receive, send) │ │ │ │ └ <bound method WebSocketProtocol.asgi_send of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7f03... │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ └ <starlette.middleware.sessions.SessionMiddleware object at 0x7f03a91c9b50> └ <starlette.middleware.errors.ServerErrorMiddleware object at 0x7f03a8f85d10> File "/usr/local/lib/python3.11/site-packages/starlette/middleware/sessions.py", line 85, in __call__ await self.app(scope, receive, send_wrapper) │ │ │ │ └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0> │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ └ <starlette.middleware.cors.CORSMiddleware object at 0x7f03a9316710> └ <starlette.middleware.sessions.SessionMiddleware object at 0x7f03a91c9b50> File "/usr/local/lib/python3.11/site-packages/starlette/middleware/cors.py", line 77, in __call__ await self.app(scope, receive, send) │ │ │ │ └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0> │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x7f03a9316310> └ <starlette.middleware.cors.CORSMiddleware object at 0x7f03a9316710> File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 103, in __call__ await self.app(scope, receive, send) │ │ │ │ └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0> │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x7f03a8fd0310> └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x7f03a9316310> File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 103, in __call__ await self.app(scope, receive, send) │ │ │ │ └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0> │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x7f03a96df950> └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x7f03a8fd0310> File "/usr/local/lib/python3.11/site-packages/starlette/middleware/base.py", line 103, in __call__ await self.app(scope, receive, send) │ │ │ │ └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0> │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ └ <open_webui.main.APIKeyRestrictionMiddleware object at 0x7f03a92d1450> └ <starlette.middleware.base.BaseHTTPMiddleware object at 0x7f03a96df950> [Previous line repeated 3 more times] File "/usr/local/lib/python3.11/site-packages/starlette_compress/__init__.py", line 86, in __call__ return await self.app(scope, receive, send) │ │ │ │ └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0> │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ └ <member 'app' of 'CompressMiddleware' objects> └ <starlette_compress.CompressMiddleware object at 0x7f0404839f80> File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 63, in __call__ await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) │ │ │ │ │ │ └ <function SessionMiddleware.__call__.<locals>.send_wrapper at 0x7f03a4216ac0> │ │ │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ │ │ └ <starlette.websockets.WebSocket object at 0x7f03a6811810> │ │ └ <fastapi.middleware.asyncexitstack.AsyncExitStackMiddleware object at 0x7f03a9316cd0> │ └ <starlette.middleware.exceptions.ExceptionMiddleware object at 0x7f03a9428090> └ <function wrap_app_handling_exceptions at 0x7f04022d3880> File "/usr/local/lib/python3.11/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app await app(scope, receive, sender) │ │ │ └ <function wrap_app_handling_exceptions.<locals>.wrapped_app.<locals>.sender at 0x7f03a4217ec0> │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... └ <fastapi.middleware.asyncexitstack.AsyncExitStackMiddleware object at 0x7f03a9316cd0> File "/usr/local/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__ await self.app(scope, receive, send) │ │ │ │ └ <function wrap_app_handling_exceptions.<locals>.wrapped_app.<locals>.sender at 0x7f03a4217ec0> │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ └ <fastapi.routing.APIRouter object at 0x7f03a96fb710> └ <fastapi.middleware.asyncexitstack.AsyncExitStackMiddleware object at 0x7f03a9316cd0> File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 716, in __call__ await self.middleware_stack(scope, receive, send) │ │ │ │ └ <function wrap_app_handling_exceptions.<locals>.wrapped_app.<locals>.sender at 0x7f03a4217ec0> │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ └ <bound method Router.app of <fastapi.routing.APIRouter object at 0x7f03a96fb710>> └ <fastapi.routing.APIRouter object at 0x7f03a96fb710> File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 736, in app await route.handle(scope, receive, send) │ │ │ │ └ <function wrap_app_handling_exceptions.<locals>.wrapped_app.<locals>.sender at 0x7f03a4217ec0> │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ └ <function Mount.handle at 0x7f04022f5800> └ Mount(path='/ws', name='', app=<socketio.asgi.ASGIApp object at 0x7f03d81fd210>) File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 462, in handle await self.app(scope, receive, send) │ │ │ │ └ <function wrap_app_handling_exceptions.<locals>.wrapped_app.<locals>.sender at 0x7f03a4217ec0> │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ └ <socketio.asgi.ASGIApp object at 0x7f03d81fd210> └ Mount(path='/ws', name='', app=<socketio.asgi.ASGIApp object at 0x7f03d81fd210>) File "/usr/local/lib/python3.11/site-packages/engineio/async_drivers/asgi.py", line 68, in __call__ await self.engineio_server.handle_request(scope, receive, send) │ │ │ │ │ └ <function wrap_app_handling_exceptions.<locals>.wrapped_app.<locals>.sender at 0x7f03a4217ec0> │ │ │ │ └ <bound method WebSocketProtocol.asgi_receive of <uvicorn.protocols.websockets.websockets_impl.WebSocketProtocol object at 0x7... │ │ │ └ {'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': ('... │ │ └ <function AsyncServer.handle_request at 0x7f03d7fa4cc0> │ └ <socketio.async_server.AsyncServer object at 0x7f03d8202390> └ <socketio.asgi.ASGIApp object at 0x7f03d81fd210> File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 452, in handle_request return await self.eio.handle_request(*args, **kwargs) │ │ │ │ └ {} │ │ │ └ ({'type': 'websocket', 'asgi': {'version': '3.0', 'spec_version': '2.4'}, 'http_version': '1.1', 'scheme': 'wss', 'server': (... │ │ └ <function AsyncServer.handle_request at 0x7f03d80fd800> │ └ <engineio.async_server.AsyncServer object at 0x7f03d821ee10> └ <socketio.async_server.AsyncServer object at 0x7f03d8202390> File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 282, in handle_request r = await self._handle_connect(environ, transport, │ │ │ └ 'websocket' │ │ └ {'wsgi.input': <engineio.async_drivers.asgi.translate_request.<locals>.AwaitablePayload object at 0x7f03a68125d0>, 'wsgi.erro... │ └ <function AsyncServer._handle_connect at 0x7f03d80fdd00> └ <engineio.async_server.AsyncServer object at 0x7f03d821ee10> File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 473, in _handle_connect ret = await s.handle_get_request(environ) │ │ └ {'wsgi.input': <engineio.async_drivers.asgi.translate_request.<locals>.AwaitablePayload object at 0x7f03a68125d0>, 'wsgi.erro... │ └ <function AsyncSocket.handle_get_request at 0x7f03d80fcfe0> └ <engineio.async_socket.AsyncSocket object at 0x7f03a8efb6d0> File "/usr/local/lib/python3.11/site-packages/engineio/async_socket.py", line 91, in handle_get_request return await getattr(self, '_upgrade_' + transport)(environ) │ │ └ {'wsgi.input': <engineio.async_drivers.asgi.translate_request.<locals>.AwaitablePayload object at 0x7f03a68125d0>, 'wsgi.erro... │ └ 'websocket' └ <engineio.async_socket.AsyncSocket object at 0x7f03a8efb6d0> File "/usr/local/lib/python3.11/site-packages/engineio/async_socket.py", line 149, in _upgrade_websocket return await ws(environ) │ └ {'wsgi.input': <engineio.async_drivers.asgi.translate_request.<locals>.AwaitablePayload object at 0x7f03a68125d0>, 'wsgi.erro... └ <engineio.async_drivers.asgi.WebSocket object at 0x7f03a6832690> File "/usr/local/lib/python3.11/site-packages/engineio/async_drivers/asgi.py", line 258, in __call__ await self.handler(self) │ │ └ <engineio.async_drivers.asgi.WebSocket object at 0x7f03a6832690> │ └ <bound method AsyncSocket._websocket_handler of <engineio.async_socket.AsyncSocket object at 0x7f03a8efb6d0>> └ <engineio.async_drivers.asgi.WebSocket object at 0x7f03a6832690> File "/usr/local/lib/python3.11/site-packages/engineio/async_socket.py", line 260, in _websocket_handler await self.close(wait=False, abort=True, │ └ <function AsyncSocket.close at 0x7f03d80fd120> └ <engineio.async_socket.AsyncSocket object at 0x7f03a8efb6d0> File "/usr/local/lib/python3.11/site-packages/engineio/async_socket.py", line 120, in close await self.server._trigger_event( │ │ └ <function AsyncServer._trigger_event at 0x7f03d80fdda0> │ └ <engineio.async_server.AsyncServer object at 0x7f03d821ee10> └ <engineio.async_socket.AsyncSocket object at 0x7f03a8efb6d0> File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 532, in _trigger_event ret = await run_async_handler() └ <function AsyncServer._trigger_event.<locals>.run_async_handler at 0x7f03a41e7ec0> > File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 515, in run_async_handler return await self.handlers[event](args[0]) │ │ │ └ ('_-X0oizlPtoY3yy7AAAA', 'transport close') │ │ └ 'disconnect' │ └ {'connect': <bound method AsyncServer._handle_eio_connect of <socketio.async_server.AsyncServer object at 0x7f03d8202390>>, '... └ <engineio.async_server.AsyncServer object at 0x7f03d821ee10> TypeError: AsyncServer._handle_eio_disconnect() missing 1 required positional argument: 'reason' ``` ### Additional Information I suspect the issue has been introduced by https://github.com/open-webui/open-webui/pull/19871 / https://github.com/open-webui/open-webui/commit/8e661a4e73ca04a6fcf958245fdeba737224a7ca , because it does not happen with Open WebUI 0.6.40, which does not contain that commit yet and nothing else seems related. Also, here is what GPT-5.2-Codex had to say when fed with the logs and repo as context: >Draft issue report (code‑backed, concise) > >Title: Redis Sentinel: scan_iter wrapped as coroutine breaks async iteration; Socket.IO disconnect handler signature mismatch > >Details > > >When WEBSOCKET_MANAGER == "redis", REDIS is created with get_redis_connection(..., async_mode=True) and passed into YdocManager. main.py (lines 105-162) > >If Sentinel hosts are configured, get_redis_connection returns SentinelRedisProxy instead of a direct Redis client. redis.py (lines 153-173) > >In async mode, SentinelRedisProxy.__getattr__ wraps every method in an async def _wrapped and returns that wrapper. This makes self._redis.scan_iter(...) a coroutine. redis.py (lines 33-73) > >YdocManager.remove_user_from_all_documents uses async for key in self._redis.scan_iter(...) without awaiting the coroutine. In Python, async for requires an object with __aiter__, so this will raise TypeError: 'async for' requires an object with __aiter__ method, got coroutine and trigger the “coroutine was never awaited” warning. utils.py (lines 191-205) > >The Socket.IO disconnect handler is defined as async def disconnect(sid) (single positional argument), but the provided log shows it is invoked with two positional arguments (sid, reason). That mismatch raises TypeError: disconnect() takes 1 positional argument but 2 were given. main.py (lines 682-688)
GiteaMirror added the bug label 2026-04-20 01:25:16 -05:00
Author
Owner

@owui-terminator[bot] commented on GitHub (Dec 23, 2025):

🔍 Similar Issues Found

I found some existing issues that might be related to this one. Please check if any of these are duplicates or contain helpful solutions:

  1. #19662 issue: Cannot logout properly
    by man2004 • Dec 01, 2025 • bug

  2. #19805 issue: did we lose most of docling configuration options in v0.6.41?
    by eleaner • Dec 08, 2025 • bug

  3. #19974 issue: DOCX not parsed correctly in temporary chat
    by sspeekenbrink • Dec 15, 2025 • bug

  4. #14521 issue:batch deleting users
    by Wiziechen • May 30, 2025 • bug

  5. #20092 issue:
    by VideoRyan • Dec 22, 2025 • bug

Show 5 more related issues
  1. #19877 issue:
    by dotmobo • Dec 11, 2025 • bug

  2. #19861 issue:
    by QuitHub • Dec 10, 2025 • bug

  3. #20019 issue:
    by j63440490 • Dec 17, 2025 • bug

  4. #17786 issue: LDAP connections are not closed
    by NegaScout • Sep 26, 2025 • bug

  5. #15671 issue: active directory users can't delete chats (or chat folders)
    by mmars13 • Jul 12, 2025 • bug


💡 Tips:

  • If this is a duplicate, please consider closing this issue and adding any additional details to the existing one
  • If you found a solution in any of these issues, please share it here to help others

This comment was generated automatically by a bot. Please react with a 👍 if this comment was helpful, or a 👎 if it was not.

<!-- gh-comment-id:3687442671 --> @owui-terminator[bot] commented on GitHub (Dec 23, 2025): 🔍 **Similar Issues Found** I found some existing issues that might be related to this one. Please check if any of these are duplicates or contain helpful solutions: 1. [#19662](https://github.com/open-webui/open-webui/issues/19662) **issue: Cannot logout properly** *by man2004 • Dec 01, 2025 • `bug`* 2. [#19805](https://github.com/open-webui/open-webui/issues/19805) **issue: did we lose most of docling configuration options in v0.6.41?** *by eleaner • Dec 08, 2025 • `bug`* 3. [#19974](https://github.com/open-webui/open-webui/issues/19974) **issue: DOCX not parsed correctly in temporary chat** *by sspeekenbrink • Dec 15, 2025 • `bug`* 4. [#14521](https://github.com/open-webui/open-webui/issues/14521) **issue:batch deleting users** *by Wiziechen • May 30, 2025 • `bug`* 5. [#20092](https://github.com/open-webui/open-webui/issues/20092) **issue:** *by VideoRyan • Dec 22, 2025 • `bug`* <details> <summary>Show 5 more related issues</summary> 6. [#19877](https://github.com/open-webui/open-webui/issues/19877) **issue:** *by dotmobo • Dec 11, 2025 • `bug`* 7. [#19861](https://github.com/open-webui/open-webui/issues/19861) **issue:** *by QuitHub • Dec 10, 2025 • `bug`* 8. [#20019](https://github.com/open-webui/open-webui/issues/20019) **issue:** *by j63440490 • Dec 17, 2025 • `bug`* 9. [#17786](https://github.com/open-webui/open-webui/issues/17786) **issue: LDAP connections are not closed** *by NegaScout • Sep 26, 2025 • `bug`* 10. [#15671](https://github.com/open-webui/open-webui/issues/15671) **issue: active directory users can't delete chats (or chat folders)** *by mmars13 • Jul 12, 2025 • `bug`* </details> --- 💡 **Tips:** - If this is a duplicate, please consider closing this issue and adding any additional details to the existing one - If you found a solution in any of these issues, please share it here to help others *This comment was generated automatically by a bot.* Please react with a 👍 if this comment was helpful, or a 👎 if it was not.
Author
Owner

@Ithanil commented on GitHub (Dec 23, 2025):

One possible fix (reverting to previous code for Redis Sentinel):

diff --git a/backend/open_webui/socket/main.py b/backend/open_webui/socket/main.py
index 72b2761c6..396ee1920 100644
--- a/backend/open_webui/socket/main.py
+++ b/backend/open_webui/socket/main.py
@@ -158,6 +158,7 @@ else:
 
 YDOC_MANAGER = YdocManager(
     redis=REDIS,
+    redis_sentinel=bool(WEBSOCKET_SENTINEL_HOSTS),
     redis_key_prefix=f"{REDIS_KEY_PREFIX}:ydoc:documents",
 )
 
diff --git a/backend/open_webui/socket/utils.py b/backend/open_webui/socket/utils.py
index 327348626..c1b9783f2 100644
--- a/backend/open_webui/socket/utils.py
+++ b/backend/open_webui/socket/utils.py
@@ -121,11 +121,13 @@ class YdocManager:
     def __init__(
         self,
         redis=None,
+        redis_sentinel=False,
         redis_key_prefix: str = f"{REDIS_KEY_PREFIX}:ydoc:documents",
     ):
         self._updates = {}
         self._users = {}
         self._redis = redis
+        self._redis_sentinel = redis_sentinel
         self._redis_key_prefix = redis_key_prefix
 
     async def append_to_updates(self, document_id: str, update: bytes):
@@ -190,11 +192,15 @@ class YdocManager:
 
     async def remove_user_from_all_documents(self, user_id: str):
         if self._redis:
-            keys = []
-            async for key in self._redis.scan_iter(
-                match=f"{self._redis_key_prefix}:*", count=100
-            ):
-                keys.append(key)
+            if self._redis_sentinel:
+                # scan method below is incompatible with current Sentinel implementation
+                keys = await self._redis.keys(f"{self._redis_key_prefix}:*")
+            else:
+                keys = []
+                async for key in self._redis.scan_iter(
+                    match=f"{self._redis_key_prefix}:*", count=100
+                ):
+                    keys.append(key)
             for key in keys:
                 if key.endswith(":users"):
                     await self._redis.srem(key, user_id)

Obviously works, because the previous code worked. But not nice.

Another more elegant possibility (to be tested):
EDIT: Tested, works too.

diff --git a/backend/open_webui/socket/utils.py b/backend/open_webui/socket/utils.py
index 327348626..00a81069b 100644
--- a/backend/open_webui/socket/utils.py
+++ b/backend/open_webui/socket/utils.py
@@ -1,5 +1,6 @@
 import json
 import uuid
+import inspect
 from open_webui.utils.redis import get_redis_connection
 from open_webui.env import REDIS_KEY_PREFIX
 from typing import Optional, List, Tuple
@@ -190,10 +191,13 @@ class YdocManager:
 
     async def remove_user_from_all_documents(self, user_id: str):
         if self._redis:
-            keys = []
-            async for key in self._redis.scan_iter(
+            keys_iter = self._redis.scan_iter(
                 match=f"{self._redis_key_prefix}:*", count=100
-            ):
+            )
+            if inspect.iscoroutine(keys_iter):
+                keys_iter = await keys_iter
+            keys = []
+            async for key in keys_iter:
                 keys.append(key)
             for key in keys:
                 if key.endswith(":users"):

Third option would be to see how this can be handled in SentinelRedisProxy (which I still think shouldn't be necessary in the first place), but I won't explore that option.

<!-- gh-comment-id:3687609109 --> @Ithanil commented on GitHub (Dec 23, 2025): One possible fix (reverting to previous code for Redis Sentinel): ``` diff --git a/backend/open_webui/socket/main.py b/backend/open_webui/socket/main.py index 72b2761c6..396ee1920 100644 --- a/backend/open_webui/socket/main.py +++ b/backend/open_webui/socket/main.py @@ -158,6 +158,7 @@ else: YDOC_MANAGER = YdocManager( redis=REDIS, + redis_sentinel=bool(WEBSOCKET_SENTINEL_HOSTS), redis_key_prefix=f"{REDIS_KEY_PREFIX}:ydoc:documents", ) diff --git a/backend/open_webui/socket/utils.py b/backend/open_webui/socket/utils.py index 327348626..c1b9783f2 100644 --- a/backend/open_webui/socket/utils.py +++ b/backend/open_webui/socket/utils.py @@ -121,11 +121,13 @@ class YdocManager: def __init__( self, redis=None, + redis_sentinel=False, redis_key_prefix: str = f"{REDIS_KEY_PREFIX}:ydoc:documents", ): self._updates = {} self._users = {} self._redis = redis + self._redis_sentinel = redis_sentinel self._redis_key_prefix = redis_key_prefix async def append_to_updates(self, document_id: str, update: bytes): @@ -190,11 +192,15 @@ class YdocManager: async def remove_user_from_all_documents(self, user_id: str): if self._redis: - keys = [] - async for key in self._redis.scan_iter( - match=f"{self._redis_key_prefix}:*", count=100 - ): - keys.append(key) + if self._redis_sentinel: + # scan method below is incompatible with current Sentinel implementation + keys = await self._redis.keys(f"{self._redis_key_prefix}:*") + else: + keys = [] + async for key in self._redis.scan_iter( + match=f"{self._redis_key_prefix}:*", count=100 + ): + keys.append(key) for key in keys: if key.endswith(":users"): await self._redis.srem(key, user_id) ``` Obviously works, because the previous code worked. But not nice. Another more elegant possibility (to be tested): EDIT: Tested, works too. ``` diff --git a/backend/open_webui/socket/utils.py b/backend/open_webui/socket/utils.py index 327348626..00a81069b 100644 --- a/backend/open_webui/socket/utils.py +++ b/backend/open_webui/socket/utils.py @@ -1,5 +1,6 @@ import json import uuid +import inspect from open_webui.utils.redis import get_redis_connection from open_webui.env import REDIS_KEY_PREFIX from typing import Optional, List, Tuple @@ -190,10 +191,13 @@ class YdocManager: async def remove_user_from_all_documents(self, user_id: str): if self._redis: - keys = [] - async for key in self._redis.scan_iter( + keys_iter = self._redis.scan_iter( match=f"{self._redis_key_prefix}:*", count=100 - ): + ) + if inspect.iscoroutine(keys_iter): + keys_iter = await keys_iter + keys = [] + async for key in keys_iter: keys.append(key) for key in keys: if key.endswith(":users"): ``` Third option would be to see how this can be handled in `SentinelRedisProxy` (which I still think shouldn't be necessary in the first place), but I won't explore that option.
Author
Owner

@Ithanil commented on GitHub (Dec 23, 2025):

Third option would be to see how this can be handled in SentinelRedisProxy (which I still think shouldn't be necessary in the first place), but I won't explore that option.

Actually I explored option 3 and have a fix ready: https://github.com/open-webui/open-webui/pull/20145

<!-- gh-comment-id:3687917625 --> @Ithanil commented on GitHub (Dec 23, 2025): >Third option would be to see how this can be handled in SentinelRedisProxy (which I still think shouldn't be necessary in the first place), but I won't explore that option. Actually I explored option 3 and have a fix ready: https://github.com/open-webui/open-webui/pull/20145
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#19099