Matrix cron delivery missing HTML/markdown formatting

NousResearch/hermes-agent · cluster 5233 · cron

Data as of

Nothing open on GitHub. Fix merged in #38649. Check it covers your case.

The short answer

0open issues
0open PRs
5items grouped
Known?
Yes. 5 related items grouped here, 2 issues already closed.
Fix event
A fix was merged to main in PR #38649. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this partially resolved, deciding PR #38649. That is separate from the fix event.
Still open
Nothing open on GitHub.

A cluster of bug reports and fixes addressing Matrix notifications sent as plain text without formatted_body or markdown rendering, traced to a missing markdown dependency in the default Docker image.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #38649 fix(deps): promote markdown to a core dependency so rich delivery works out of the box (#32486)

    merged to main

    files: gateway/platforms/matrix.py, pyproject.toml, tools/lazy_deps.py

    Show the diff
    • gateway/platforms/matrix.py
      @@ -2799,11 +2799,11 @@ def _mxc_to_http(self, mxc_url: str) -> str:
           def _markdown_to_html(self, text: str) -> str:
               """Convert Markdown to Matrix-compatible HTML (org.matrix.custom.html).
       
      -        Uses the ``markdown`` library when available (installed with the
      -        ``matrix`` extra).  Falls back to a comprehensive regex converter
      -        that handles fenced code blocks, inline code, headers, bold,
      -        italic, strikethrough, links, blockquotes, lists, and horizontal
      -        rules — everything the Matrix HTML spec allows.
      +        Uses the ``markdown`` library (a core dependency) when available.
      +        Falls back to a comprehensive regex converter that handles fenced
      +        code blocks, inline code, headers, bold, italic, strikethrough,
      +        links, blockquotes, lists, and horizontal rules — everything the
      +        Matrix HTML spec allows.
               """
               try:
                   import markdown as _md
    • pyproject.toml
      @@ -61,6 +61,16 @@ dependencies = [
         "prompt_toolkit==3.0.52",
         # Cron scheduler (built-in feature — scheduled cron/interval jobs use croniter).
         "croniter==6.0.0",
      +  # Markdown -> HTML conversion for rich message delivery (Matrix
      +  # `formatted_body`, and the `send_message` tool's HTML path). Now on the
      +  # DEFAULT delivery path, not matrix-specific: without it both
      +  # gateway/platforms/matrix.py and tools/send_message_tool.py silently fall
      +  # back to plain text, so cron/agent deliveries render raw `##`/`**`/tables
      +  # in clients like Element (see #32486). Pure-Python py3-none-any wheel
      +  # (~108KB, no compiled extensions, no platform constraints), so unlike the
      +  # matrix extra's `mautrix`/`python-olm` it's safe to ship everywhere — keeps
      +  # it out of the lazy-install path that exists only for the heavy matrix deps.
      +  "Markdown==3.10.2",
         # Skills Hub (GitHub App JWT auth — optional, only needed for bot identity)
         "PyJWT[crypto]==2.12.1",  # CVE-2026-32597
         # Windows has no IANA tzdata shipped with the OS, so Python's ``zoneinfo``
      @@ -104,7 +114,7 @@ dev = ["debugpy==1.8.20", "pytest==9.0.2", "pytest-asyncio==1.3.0", "pytest-time
       messaging = ["python-telegram-bot[webhooks]==22.6", "discord.py[voice]==2.7.1", "aiohttp==3.13.3", "brotlicffi==1.2.0.1", "slack-bolt==1.27.0", "slack-sdk==3.40.1", "qrcode==7.4.2"]
       cron = []  # croniter is now a core dependency; this extra kept for back-compat
       slack = ["slack-bolt==1.27.0", "slack-sdk==3.40.1", "aiohttp==3.13.3"]
      -matrix = ["mautrix[encryption]==0.21.0", "Markdown==3.10.2", "aiosqlite==0.22.1", "asyncpg==0.31.0", "aiohttp-socks==0.11.0"]
      +matrix = ["mautrix[encryption]==0.21.0", "aiosqlite==0.22.1", "asyncpg==0.31.0", "aiohttp-socks==0.11.0"]
       # WeCom callback-mode adapter — parses untrusted XML POST bodies from
       # WeCom-controlled callback endpoints, so we use defusedxml (drop-in
       # replacement for stdlib xml.etree.ElementTree) to block billion-laughs
    • tools/lazy_deps.py
      @@ -135,7 +135,6 @@
           ),
           "platform.matrix": (
               "mautrix[encryption]==0.21.0",
      -        "Markdown==3.10.2",
               "aiosqlite==0.22.1",
               "asyncpg==0.31.0",
               "aiohttp-socks==0.11.0",
    • uv.lock
      @@ -1398,6 +1398,7 @@ dependencies = [
           { name = "fire" },
           { name = "httpx", extra = ["socks"] },
           { name = "jinja2" },
      +    { name = "markdown" },
           { name = "openai" },
           { name = "pathspec" },
           { name = "prompt-toolkit" },
      @@ -1509,7 +1510,6 @@ matrix = [
           { name = "aiohttp-socks" },
           { name = "aiosqlite" },
           { name = "asyncpg" },
      -    { name = "markdown" },
           { name = "mautrix", extra = ["encryption"] },
       ]
       mcp = [
      @@ -1647,7 +1647,7 @@ requires-dist = [
           { name = "httpx", extras = ["socks"], specifier = "==0.28.1" },
           { name = "jinja2", specifier = "==3.1.6" },
           { name = "lark-oapi", marker = "extra == 'feishu'", specifier = "==1.5.3" },
      -    { name = "markdown", marker = "extra == 'matrix'", specifier = "==3.10.2" },
      +    { name = "markdown", specifier = "==3.10.2" },
           { name = "mautrix", extras = ["encryption"], marker = "extra == 'matrix'", specifier = "==0.21.0" },
           { name = "mcp", marker = "extra == 'computer-use'", specifier = "==1.26.0" },
           { name = "mcp", marker = "extra == 'dev'", specifier = "==1.26.0" },

2 PRs closed without merging

From the close comments: stale or abandoned 1 · other 1

  • #5236 fix(matrix): add formatted_body with HTML rendering for cron deliveries · other → #5271
  • #32492 fix(tools): regex-fallback Matrix formatted_body when markdown lib missing · stale or abandoned
Closed issues · 2
  • Closed#5233[Bug]: Cron delivery to Matrix sends plain text — no formatted_body or markdown rendering
  • Closed#32486[Bug]: Default Docker image missing `markdown` dep — cron delivery silently falls back to plain text despite PR #5271
How this cluster was grouped
  • Link evidence (partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
  • Members outside that structural core may belong to a different underlying problem.
  • Pairwise link density 0.34: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
cron
Merged PRs
#38649
Closed issues
2
Attempts
2 PRs closed without merging
Fix link checked
4 Jun 2026

For your agent

The same facts, typed.