fix(weixin): skip live adapter reuse in send_weixin_direct when on a different event loop#12810
fix(weixin): skip live adapter reuse in send_weixin_direct when on a different event loop#12810yueyefengs wants to merge 1 commit into
Conversation
…reuse The previous attempt used connector._loop to detect cross-loop reuse, but aiohttp 3.9+ no longer stores _loop on the connector, so the guard always evaluated to True and the bug persisted. Instead, store asyncio.get_running_loop() as self._event_loop in connect(), then compare it against the caller's running loop in send_weixin_direct() before deciding whether to reuse the live adapter's session. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2b533a4 to
bf020c2
Compare
1 similar comment
|
Confirming reproduction and verifying this fix shape on recent main:
I applied a functionally-equivalent local patch to Minor naming nit on this PR: caching For the maintainers triaging the cluster of similar PRs (#13520, #14530, #14873, #15911, #16790, #17267, #17557): all that I've read converge on the same one-liner — gate the live-adapter reuse on loop affinity. This PR is the earliest and one of the simpler diffs. |
Problem
When the cron scheduler's live adapter delivery fails (e.g.
ret=-2network error), it falls back to a standalone path that runs_send_to_platforminsideasyncio.run()in a thread-pool executor — a fresh event loop.send_weixin_directthen finds the live adapter in_LIVE_ADAPTERSand unconditionally reuses itsaiohttp.ClientSession, which was created on the gateway event loop. Using that session from a different loop causes aiohttp's internalasyncio.timeout()to raise:because
asyncio.timeout()(Python 3.11+) checksasyncio.current_task(), and while a Task does exist on the new loop, the connector is bound to the old one — the mismatch triggers the assertion.The result is three retried failures followed by a
delivery errorlog, and the cron message never reaches the user.Fix
Before reusing the live adapter session in
send_weixin_direct, compareconnector._loopagainstasyncio.get_running_loop(). If they differ, skip the live adapter branch and fall through to the existing fresh-session path (async with aiohttp.ClientSession(...)).The live adapter fast-path is preserved for all callers that are already on the correct loop (e.g. the
/send_messagetool running inside the gateway).Test Plan
pytest tests/gateway/test_weixin.py— 42 passed, 0 failuresret=-2, triggering the standalone fallback path