fix(feishu): isolate lark ws Client event loop from main asyncio loop

Commit 0209ad5 moved `import lark_oapi as lark` inside the start()
method (lazy import) to suppress DeprecationWarnings. This had an
unintended side effect: the import now happens after the main asyncio
loop is already running, so lark_oapi's module-level

    loop = asyncio.get_event_loop()

captures the running main loop. When the WebSocket thread then calls
loop.run_until_complete() inside Client.start(), Python raises:

    RuntimeError: This event loop is already running

and the _connect/_disconnect coroutines are never awaited.

Fix: in run_ws(), create a fresh event loop with asyncio.new_event_loop(),
set it as the thread's current loop, and patch lark_oapi.ws.client.loop
to point to this dedicated loop before calling Client.start(). The loop
is closed on thread exit.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
coldxiangyu
2026-03-05 17:27:17 +08:00
parent fb74281434
commit 97522bfa03

View File

@@ -290,16 +290,28 @@ class FeishuChannel(BaseChannel):
log_level=lark.LogLevel.INFO
)
# Start WebSocket client in a separate thread with reconnect loop
# Start WebSocket client in a separate thread with reconnect loop.
# A dedicated event loop is created for this thread so that lark_oapi's
# module-level `loop = asyncio.get_event_loop()` picks up an idle loop
# instead of the already-running main asyncio loop, which would cause
# "This event loop is already running" errors.
def run_ws():
while self._running:
try:
self._ws_client.start()
except Exception as e:
logger.warning("Feishu WebSocket error: {}", e)
if self._running:
import time
time.sleep(5)
import time
import lark_oapi.ws.client as _lark_ws_client
ws_loop = asyncio.new_event_loop()
asyncio.set_event_loop(ws_loop)
# Patch the module-level loop used by lark's ws Client.start()
_lark_ws_client.loop = ws_loop
try:
while self._running:
try:
self._ws_client.start()
except Exception as e:
logger.warning("Feishu WebSocket error: {}", e)
if self._running:
time.sleep(5)
finally:
ws_loop.close()
self._ws_thread = threading.Thread(target=run_ws, daemon=True)
self._ws_thread.start()