larksuite / larksuite/oapi-sdk-python

[Python lark-oapi 1.6.7] ws/client.py module-level event loop causes RuntimeError when first imported inside a running loop

Open
#133 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
559
Forks
102
PR merge metrics
No merged PRs in 30d

Description

Environment

  • Package: lark-oapi 1.6.7 (Python)
  • Python: 3.9+
  • Affected path: lark_oapi/ws/client.py

Summary

lark_oapi/ws/client.py captures the event loop at module import time via a module-level global. If the module is first imported while an event loop is already running (e.g. inside asyncio.run()), the subsequent loop.run_until_complete(...) raises RuntimeError: This event loop is already running, and the WebSocket long-connection fails immediately.

Root Cause

client.py lines 30-34 run at import time:

try:
    loop = asyncio.get_event_loop()
except RuntimeError:
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

Under Python 3.9, asyncio.get_event_loop() returns the currently running loop when called from within a coroutine / asyncio.run() context, so the module-level loop captures an already-running loop.

client.py then uses this global in start():

# client.py:163, 169, 171, 176
loop.run_until_complete(self._connect())
...
loop.run_until_complete(_select())

Calling run_until_complete on an already-running loop raises RuntimeError: This event loop is already running.

Reproduction

import asyncio

async def main():
    from lark_oapi.ws.client import Client  # first import here captures the running loop
    Client(...).start()                      # RuntimeError: This event loop is already running

asyncio.run(main())

Real-world Trigger

Host applications that lazily import lark_oapi.channel (which transitively imports lark_oapi.ws.client) from within their asyncio main loop hit this unconditionally on first startup.

Suggested Fix

Remove the module-level loop global; create a dedicated event loop per Client instance inside the worker thread in start(), isolating the WS client from the caller's event loop.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with lark_oapi/ws/client.py, especially the module-level loop setup at lines 30-34 and the run_until_complete calls around lines 163, 169, 171, and 176. Run the provided asyncio.run reproduction, then trace Client.start() and its worker-thread behavior. Done means the client can be first imported and started inside a running asyncio loop without the RuntimeError.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
networking
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.