hermes - 💡(How to fix) Fix [Bug] Weixin send_message fails with 'Timeout context manager should be used inside a task' [3 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#18791Fetched 2026-05-03 04:54:17
View on GitHub
Comments
3
Participants
2
Timeline
8
Reactions
0
Author
Timeline (top)
labeled ×4commented ×3closed ×1

Error Message

  1. Observe the error

Root Cause

The call chain is:

send_message tool (sync handler)
  → _run_async() in model_tools.py
    → detects running event loop → spawns new thread
      → creates new event loop → loop.run_until_complete(coro)
        → send_weixin_direct() in weixin.py
          → adapter.send() / _send_file()
            → aiohttp.ClientSession.post(timeout=aiohttp.ClientTimeout(...))

In _run_async() (model_tools.py:82), when called from inside the gateway's async context, it spawns a new thread and creates a fresh event loop. The coroutine runs via loop.run_until_complete(coro).

The problem: aiohttp's ClientTimeout context manager internally calls asyncio.current_task() and raises RuntimeError("Timeout context manager should be used inside a task") if no task is found. While run_until_complete does wrap the coroutine in a task, there appears to be a timing or scoping issue where the ClientTimeout.__enter__ is invoked outside the task context — likely during the fallback path in send_weixin_direct() (weixin.py:2058) when creating a temporary aiohttp.ClientSession, or during session cleanup/initialization.

Fix Action

Workaround

Currently no known workaround. Text replies through the gateway's normal reply path work fine — only send_message tool calls and cron delivery are affected.

Code Example

Timeout context manager should be used inside a task

---

send_message tool (sync handler)
_run_async() in model_tools.py
    → detects running event loop → spawns new thread
      → creates new event loop → loop.run_until_complete(coro)
send_weixin_direct() in weixin.py
          → adapter.send() / _send_file()
            → aiohttp.ClientSession.post(timeout=aiohttp.ClientTimeout(...))
RAW_BUFFERClick to expand / collapse

Bug Description

When using the send_message tool to send text, files, or images via the Weixin (WeChat) platform, the operation consistently fails with:

Timeout context manager should be used inside a task

This affects all outbound Weixin delivery through the send_message tool (text, file attachments, images, and documents).

Environment

  • Hermes Agent: v0.12.0 (2026.4.30)
  • Platform: Weixin (WeChat) via iLink
  • OS: Arch Linux (x86_64)
  • aiohttp: installed via venv

Steps to Reproduce

  1. Configure Hermes with Weixin platform (iLink)
  2. Start the gateway (hermes gateway run)
  3. From any session, call send_message(target="weixin", message="test")
  4. Observe the error

Root Cause Analysis

The call chain is:

send_message tool (sync handler)
  → _run_async() in model_tools.py
    → detects running event loop → spawns new thread
      → creates new event loop → loop.run_until_complete(coro)
        → send_weixin_direct() in weixin.py
          → adapter.send() / _send_file()
            → aiohttp.ClientSession.post(timeout=aiohttp.ClientTimeout(...))

In _run_async() (model_tools.py:82), when called from inside the gateway's async context, it spawns a new thread and creates a fresh event loop. The coroutine runs via loop.run_until_complete(coro).

The problem: aiohttp's ClientTimeout context manager internally calls asyncio.current_task() and raises RuntimeError("Timeout context manager should be used inside a task") if no task is found. While run_until_complete does wrap the coroutine in a task, there appears to be a timing or scoping issue where the ClientTimeout.__enter__ is invoked outside the task context — likely during the fallback path in send_weixin_direct() (weixin.py:2058) when creating a temporary aiohttp.ClientSession, or during session cleanup/initialization.

Possible Fixes

  1. Wrap in asyncio.create_task(): Ensure the aiohttp calls always happen inside an explicitly created task.
  2. Use asyncio.timeout instead of aiohttp.ClientTimeout: Replace aiohttp.ClientTimeout(total=...) with Python 3.11+ asyncio.timeout().
  3. Ensure _run_async always provides a task context: Modify run_until_complete path to guarantee task context for all async operations.

Workaround

Currently no known workaround. Text replies through the gateway's normal reply path work fine — only send_message tool calls and cron delivery are affected.

extent analysis

TL;DR

The most likely fix involves ensuring that aiohttp calls are made within an explicitly created task context to resolve the "Timeout context manager should be used inside a task" error.

Guidance

  • Verify Task Context: Confirm that the aiohttp calls, specifically those using ClientTimeout, are executed within a task created by asyncio.create_task() to ensure compatibility with aiohttp.ClientTimeout.
  • Explore asyncio.timeout: Consider replacing aiohttp.ClientTimeout with asyncio.timeout() for Python 3.11+, as it might offer better integration with the asyncio task context.
  • Review _run_async: Modify the _run_async function to guarantee that all async operations, including those in the send_weixin_direct path, are executed within a task context.
  • Test with Simplified Scenarios: Before applying fixes to the production environment, test them with simplified scenarios to isolate the root cause and verify the effectiveness of the proposed solutions.

Example

import asyncio

# Example of ensuring a task context for aiohttp calls
async def send_weixin_direct_with_task_context():
    async def inner():
        # aiohttp calls here, e.g., session.post()
        pass
    
    task = asyncio.create_task(inner())
    await task

# Alternatively, using asyncio.timeout (Python 3.11+)
async def send_weixin_direct_with_asyncio_timeout():
    try:
        async with asyncio.timeout(total=30):  # 30 seconds timeout
            # aiohttp calls here, e.g., session.post()
            pass
    except asyncio.TimeoutError:
        # Handle timeout
        pass

Notes

The provided solutions assume that the issue is indeed related to the task context and aiohttp usage. Testing and verification are crucial to confirm the root cause and the effectiveness of the proposed fixes. Additionally, compatibility with the

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix [Bug] Weixin send_message fails with 'Timeout context manager should be used inside a task' [3 comments, 2 participants]