Skip to content

[Bug]: Matrix manual cron delivery fails with "Timeout context manager should be used inside a task" #61495

Description

@Bounz

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

  1. Run Hermes Gateway with Matrix connected.
  2. Create a cron job with deliver: "origin" or a Matrix target.
  3. From a Matrix chat, manually trigger the job via cronjob(action="run") / manual cron run.
  4. 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

https://paste.rs/qpAdw

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?

  • I'd like to fix this myself and submit a PR

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existscomp/cronCron scheduler and job managementcomp/gatewayGateway runner, session dispatch, deliveryplatform/matrixMatrix adapter (E2EE)sweeper:risk-message-deliverySweeper risk: may drop, duplicate, misroute, or suppress messagestype/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions