hermes - 💡(How to fix) Fix Mattermost: media attachments (generated images/files) post to channel root instead of threading under the triggering message

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…

On the Mattermost gateway, media attachments (generated images, files, voice, video) are posted to the channel root instead of being threaded under the triggering message, even when the bot is configured with threaded replies (MATTERMOST_REPLY_MODE=thread / display.platforms.mattermost.tool_progress etc.). The text reply threads correctly; only the media posts land at top level. The result is that an image-generation reply shows up as two posts: the (threaded) text and a separate (un-threaded) attachment, so the channel "loses" the attachment from the conversation thread.

Version: v2026.5.29.2. Image-gen backend: openai-codex (gpt-image-2).

Root Cause

Root cause (code)

Fix Action

Fix / Workaround

We carry a downstream overlay that adds reply_to to send_multiple_images + the _deliver_media_from_response call sites. It cleanly fixes a related cosmetic bug (empty ![alt]() image markdown left behind by format_message when the URL is empty), but in our environment the attachment still posted at channel root even with reply_to threaded through, so there may be an additional anchor-resolution detail on the Mattermost path worth checking. Happy to share the patch.

Code Example

_thread_meta = self._thread_metadata_for_source(event.source, self._reply_anchor_for_event(event))

---

await adapter.send_multiple_images(chat_id=event.source.chat_id, images=images, metadata=_thread_meta)
await adapter.send_document(chat_id=event.source.chat_id, file_path=..., metadata=_thread_meta)
RAW_BUFFERClick to expand / collapse

Summary

On the Mattermost gateway, media attachments (generated images, files, voice, video) are posted to the channel root instead of being threaded under the triggering message, even when the bot is configured with threaded replies (MATTERMOST_REPLY_MODE=thread / display.platforms.mattermost.tool_progress etc.). The text reply threads correctly; only the media posts land at top level. The result is that an image-generation reply shows up as two posts: the (threaded) text and a separate (un-threaded) attachment, so the channel "loses" the attachment from the conversation thread.

Version: v2026.5.29.2. Image-gen backend: openai-codex (gpt-image-2).

Repro

  1. Configure the Mattermost gateway, threaded replies enabled.
  2. In a channel (not an existing thread, i.e. thread_id is None), mention the bot: @bot draw a cat and post it.
  3. Observe: the assistant text reply is threaded under your message, but the uploaded image post is a new top-level post in the channel.

Root cause (code)

In gateway/run.py, _deliver_media_from_response() builds thread metadata via:

_thread_meta = self._thread_metadata_for_source(event.source, self._reply_anchor_for_event(event))

For a top-level Mattermost channel message source.thread_id is None, so _thread_metadata_for_source() returns None early. The media sends are then called with only metadata=_thread_meta (i.e. None) and no reply anchor:

await adapter.send_multiple_images(chat_id=event.source.chat_id, images=images, metadata=_thread_meta)
await adapter.send_document(chat_id=event.source.chat_id, file_path=..., metadata=_thread_meta)

The Mattermost adapter (plugins/platforms/mattermost/adapter.py) threads via a reply_to argument (it sets payload["root_id"] from reply_to in _send_url_as_file / _send_local_file / send), but:

  • send_multiple_images() has no reply_to parameter at all and never sets root_id.
  • The text path works because it passes reply_to=self._reply_anchor_for_event(event) (e.g. around the main reply send), whereas the media-delivery path does not.

So on Mattermost the reply anchor never reaches the attachment post.

Suggested fix

Pass the reply anchor to the media sends in _deliver_media_from_response() (and the kanban-artifact path), and give MattermostAdapter.send_multiple_images() a reply_to parameter that sets payload["root_id"] = await self._resolve_root_id(reply_to) when self._reply_mode == "thread", mirroring the other send methods.

Note

We carry a downstream overlay that adds reply_to to send_multiple_images + the _deliver_media_from_response call sites. It cleanly fixes a related cosmetic bug (empty ![alt]() image markdown left behind by format_message when the URL is empty), but in our environment the attachment still posted at channel root even with reply_to threaded through, so there may be an additional anchor-resolution detail on the Mattermost path worth checking. Happy to share the patch.

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 Mattermost: media attachments (generated images/files) post to channel root instead of threading under the triggering message