Skip to content

fix(wecom): dispatch WS SDK calls to ws_loop to fix asyncio event loop conflict#3300

Open
octo-patch wants to merge 1 commit intoagentscope-ai:mainfrom
octo-patch:fix/issue-3296-wecom-asyncio-event-loop
Open

fix(wecom): dispatch WS SDK calls to ws_loop to fix asyncio event loop conflict#3300
octo-patch wants to merge 1 commit intoagentscope-ai:mainfrom
octo-patch:fix/issue-3296-wecom-asyncio-event-loop

Conversation

@octo-patch
Copy link
Copy Markdown
Contributor

Fixes #3296

Problem

When using the WeCom channel with send_file_to_user or text replies, the SDK internally uses asyncio.ensure_future() which attaches tasks to whatever loop is currently running. Since the WebSocket loop (_ws_loop) runs in a dedicated thread separate from the main agent loop (_loop), this causes:

RuntimeError: Task ... got Future ... attached to a different loop

This prevents files/photos from being sent and causes text reply failures.

Root Cause

Two event loops are running:

  1. self._loop — main agent/logic loop
  2. self._ws_loop — WebSocket loop in a dedicated thread

In _send_ws_cmd(), asyncio.get_event_loop() may return the wrong loop for Future creation. More critically, self._client._ws_manager.send() and self._client.reply_stream() internally call asyncio.ensure_future(), attaching tasks to the main loop — but the WS operations need to run on self._ws_loop.

Solution

  • _send_ws_cmd(): Use self._loop or asyncio.get_running_loop() for Future creation. Dispatch _ws_manager.send() to self._ws_loop via asyncio.run_coroutine_threadsafe() + asyncio.wrap_future() when the WS loop is running.
  • _send_text_via_frame(): Similarly dispatch reply_stream() to self._ws_loop when available.

Both methods fall back to direct await when _ws_loop is not running (e.g. during tests or single-loop setups).

Testing

Verified by the bug reporter: after applying the fix, send_file_to_user correctly sends photos and files to users via WeCom.

@github-actions
Copy link
Copy Markdown

Welcome to QwenPaw! 🐾

Hi @octo-patch, this is your 18th Pull Request.

📋 About PR Template

To help maintainers review your PR faster, please make sure to include:

  • Description - What this PR does and why
  • Type of Change - Bug fix / Feature / Breaking change / Documentation / Refactoring
  • Component(s) Affected - Core / Console / Channels / Skills / CLI / Documentation / Tests / CI/CD / Scripts
  • Checklist:
    • Run and pass pre-commit run --all-files
    • Run and pass relevant tests (pytest or as applicable)
    • Update documentation if needed
  • Testing - How to test these changes
  • Local Verification Evidence:
    pre-commit run --all-files
    # paste summary result
    
    pytest
    # paste summary result

Complete PR information helps speed up the review process. You can edit the PR description to add these details.

🙌 Join Developer Community

Thanks so much for your contribution! We'd love to invite you to join the official QwenPaw developer group! You can find the Discord and DingTalk group links under the "Developer Community" section on our docs page:
https://qwenpaw.agentscope.io/docs/community

We truly appreciate your enthusiasm—and look forward to your future contributions! 😊

We'll review your PR soon.


Tip

⭐ If you find QwenPaw useful, please give us a Star!

Star QwenPaw

Staying ahead

Star QwenPaw on GitHub and be instantly notified of new releases.

Your star helps more developers discover this project! 🐾

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

[Bug] WeChat channel send_file_to_user fails with RuntimeError: Future attached to a different loop

1 participant