Bug Description
Manual cron job runs triggered from a live Matrix gateway session can fail during Matrix delivery:
Matrix: failed to send to !room: Timeout context manager should be used inside a task
cron.scheduler: Job '<job_id>': delivery error: Matrix send failed: Timeout context manager should be used inside a task
The job itself completes (last_status: ok); only the final Matrix delivery fails. Normal Matrix replies from the gateway continue to work.
Steps to Reproduce
- Run Hermes Gateway with Matrix connected.
- Create a cron job with
deliver: "origin" or a Matrix target.
- From a Matrix chat, manually trigger the job via
cronjob(action="run") / manual cron run.
- Observe Matrix delivery failing with
Timeout context manager should be used inside a task.
Expected Behavior
Manual cron runs from Matrix should deliver through the same event-loop-safe live adapter path as scheduled in-gateway cron ticks.
Actual Behavior
Observed log example:
ERROR hermes_plugins.matrix_platform.adapter: Matrix: failed to send to !dbDMkhSyaccmgAoXgy:domain.tld: Timeout context manager should be used inside a task
ERROR cron.scheduler: Job '5e8ef31933f8': delivery error: Matrix send failed: Timeout context manager should be used inside a task
Affected Component
Gateway (Telegram/Discord/Slack/WhatsApp)
Messaging Platform (if gateway-related)
No response
Debug Report
Operating System
Ubuntu 24.0.4.4
Python Version
3.11.15
Hermes Version
0.18.2
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
Manual cron delivery can reach this path:
cronjob(action="run")
-> cron.scheduler._deliver_result(..., adapters=None, loop=None)
-> tools.send_message_tool._send_to_platform(...)
-> tools.send_message_tool._send_via_adapter(...)
-> runner.adapters.get(matrix)
-> await adapter.send(...)
The Matrix adapter owns a live mautrix/aiohttp client created on the gateway event loop. Calling adapter.send() directly from a different loop/task context makes aiohttp raise:
Timeout context manager should be used inside a task
cron/scheduler.py::_deliver_result() already schedules live-adapter delivery onto the gateway loop when it receives adapters and loop; the manual/tool path through send_message_tool._send_via_adapter() appears to bypass that safety.
Proposed Fix (optional)
Make tools/send_message_tool.py::_send_via_adapter() event-loop aware:
- when using a live gateway adapter, schedule
adapter.send(...) onto the gateway loop via safe_schedule_threadsafe(...) instead of awaiting it directly from the caller loop;
- if no gateway loop is available, use
standalone_sender_fn only when safe;
- for Matrix/E2EE, avoid silently downgrading to unencrypted raw HTTP delivery when encryption is required.
Are you willing to submit a PR for this?
Bug Description
Manual cron job runs triggered from a live Matrix gateway session can fail during Matrix delivery:
The job itself completes (
last_status: ok); only the final Matrix delivery fails. Normal Matrix replies from the gateway continue to work.Steps to Reproduce
deliver: "origin"or a Matrix target.cronjob(action="run")/ manual cron run.Timeout context manager should be used inside a task.Expected Behavior
Manual cron runs from Matrix should deliver through the same event-loop-safe live adapter path as scheduled in-gateway cron ticks.
Actual Behavior
Observed log example:
Affected Component
Gateway (Telegram/Discord/Slack/WhatsApp)
Messaging Platform (if gateway-related)
No response
Debug Report
Operating System
Ubuntu 24.0.4.4
Python Version
3.11.15
Hermes Version
0.18.2
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
Manual cron delivery can reach this path:
The Matrix adapter owns a live mautrix/aiohttp client created on the gateway event loop. Calling
adapter.send()directly from a different loop/task context makes aiohttp raise:cron/scheduler.py::_deliver_result()already schedules live-adapter delivery onto the gateway loop when it receivesadaptersandloop; the manual/tool path throughsend_message_tool._send_via_adapter()appears to bypass that safety.Proposed Fix (optional)
Make
tools/send_message_tool.py::_send_via_adapter()event-loop aware:adapter.send(...)onto the gateway loop viasafe_schedule_threadsafe(...)instead of awaiting it directly from the caller loop;standalone_sender_fnonly when safe;Are you willing to submit a PR for this?