larksuite / larksuite/oapi-sdk-python
ws.Client: module-level loop variable prevents multiple instances (multi-bot race condition)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 559
- Forks
- 102
- PR merge metrics
- No merged PRs in 30d
Description
Problem
lark_oapi/ws/client.py uses a module-level loop variable (line 26-29):
try:
loop = asyncio.get_event_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
All ws.Client instances share this single loop reference. When multiple Feishu bots are started in separate threads (a common pattern for multi-bot applications), they overwrite each other's loop, causing RuntimeError: This event loop is already running.
Race condition
- Thread A:
ws_mod.loop = loop_A - Thread B:
ws_mod.loop = loop_B(overwrites A) - Thread A:
cli.start()→ readsws_mod.loop→ getsloop_B→loop_B.run_until_complete() - Thread B:
cli.start()→ readsws_mod.loop→ getsloop_B→ ERROR: already running
Even after connecting, _receive_message_loop (line 171) uses loop.create_task() which may reference the wrong loop.
Current workaround
We replaced ws_mod.loop with a thread-local proxy:
class _ThreadLocalLoopProxy:
def __getattr__(self, name):
return getattr(asyncio.get_event_loop(), name)
ws_mod.loop = _ThreadLocalLoopProxy()
This works but is fragile against SDK changes.
Suggested fix
Make ws.Client instance-based, like the Node.js SDK (@larksuiteoapi/node-sdk) already does with WSClient:
class Client:
def __init__(self, ...):
self._loop = asyncio.new_event_loop()
...
def start(self):
self._loop.run_until_complete(self._connect())
self._loop.create_task(self._ping_loop())
self._loop.run_until_complete(_select())
Or provide an async start_async() method (as suggested in #96) so the client can integrate with existing event loops (e.g., uvicorn, FastAPI).
Related issues
- #96 —
loopconflicts with async frameworks - #109 — concerns about production readiness
Environment
- lark_oapi version: latest (PyPI)
- Python: 3.13
- Use case: multi-bot IM middleware (each bot = separate
ws.Clientinstance in its own thread)
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in lark_oapi/ws/client.py around the module-level loop at lines 26-29, then trace Client.start() and _receive_message_loop at line 171. Reproduce the separate-thread, multiple-bot case described in the issue and determine the intended instance or async API approach. Done means independent ws.Client instances no longer share or overwrite an event loop and can run concurrently without the reported RuntimeError.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100