hermes - ✅(Solved) Fix [Bug]: Hermes can send attachment (media file) when replying in thread, but not as a direct message in channel or DM [1 pull requests, 1 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#17261Fetched 2026-04-30 06:48:47
View on GitHub
Comments
0
Participants
1
Timeline
6
Reactions
0
Author
Participants
Timeline (top)
labeled ×4cross-referenced ×1referenced ×1

Error Message

Additional Logs / Traceback (optional)

Root Cause

Root Cause Analysis (optional)

Fix Action

Fix / Workaround

In-thread replies use the adapter's full media pipeline. When the user DMs Hermes and Hermes replies inline, the gateway runs BasePlatformAdapter._process_message_background (gateway/platforms/base.py:2415-2549). That code:

  1. Calls self.extract_media(response) to find MEDIA:<path> tags.
  2. For each file, dispatches by extension to the adapter's own send_voice / send_video / send_image_file / send_document methods.

PR fix notes

PR #17348: fix(send_message): native Slack media uploads for cross-channel/DM sends (#17261)

Description (problem / solution / changelog)

Summary

  • Cross-channel send_message to Slack drops MEDIA attachments and emits "MEDIA attachments were omitted for slack…" even though SlackAdapter has full media support via files_upload_v2. This PR adds the missing media branch.
  • _send_slack now uploads files through slack_sdk.web.async_client.AsyncWebClient.files_upload_v2 when media_files is non-empty, with thread_ts forwarded for in-thread cross-channel sends.
  • The "supported platforms" warning + media-only-no-text error now list slack.

Fixes #17261.

The bug

Reporter (#17261) traced it: BasePlatformAdapter._process_message_background (in-thread agent replies) dispatches MEDIA tags through the SlackAdapter's send_image_file / send_voice / send_video / send_document (all call files_upload_v2). The send_message tool — used for cross-channel and DM sends — has its own hand-rolled per-platform helpers in tools/send_message_tool.py and just lacked a Slack-with-media branch:

  • _send_to_platform had if platform == Platform.TELEGRAM / DISCORD / MATRIX and media_files / SIGNAL and media_files branches that pass media_files through to platform-specific helpers.
  • Slack fell through to the generic "non-media platforms" block (tools/send_message_tool.py:580), which surfaced the warning at tools/send_message_tool.py:589-593 and called _send_slack(token, chat_id, chunk) — text only.

So the result the user saw — "send works in thread, fails when DMing or sending to another channel" — matches the code path exactly.

The fix

Two-part surgical change in tools/send_message_tool.py:

  1. _send_to_platform — add a Slack media branch (mirrors the existing Telegram / Discord branches). Media attaches to the last chunk only so we don't upload duplicates per-chunk on long messages:

    if platform == Platform.SLACK and media_files:
        last_result = None
        for i, chunk in enumerate(chunks):
            is_last = (i == len(chunks) - 1)
            result = await _send_slack(
                pconfig.token, chat_id, chunk,
                media_files=media_files if is_last else [],
                thread_id=thread_id,
            )
            ...
  2. _send_slack — extend signature to (token, chat_id, message, media_files=None, thread_id=None). When media_files is empty, behavior is unchanged (still uses aiohttp + chat.postMessage). When media is present, it uses slack_sdk.web.async_client.AsyncWebClient(token=token).files_upload_v2(...) per file, with the message text carried as initial_comment on the first upload only (so multi-file sends don't duplicate the caption). thread_id becomes thread_ts so the upload lands in the right thread when one is specified.

  3. The "MEDIA attachments were omitted" warning + the media-only-no-text error were updated to list slack as supported.

Approach choice: directly using AsyncWebClient(token=...) mirrors the one-shot HTTP/SDK style of the other senders in this file. Going through _send_yuanbao-style "fetch the running gateway adapter" would couple the cross-channel send to the gateway's lifecycle (vs. a clean, restartable HTTP/SDK call) and didn't seem worth the indirection — SlackAdapter._upload_file itself is just a wrapper around the same files_upload_v2 API call. The SlackAdapter is untouched.

Test plan

  • Focused regression — 10 new tests across two classes:
    • TestSendSlackMedia (6): single image, thread_ts forwarding, multi-file caption-on-first-only invariant, missing-file error, SlackApiError surfacing, and a regression that the text-only path does not import slack_sdk (gates the dep behind media_files).
    • TestSendToPlatformSlackMedia (4): media branch routing, last-chunk-only attachment for chunked messages over Slack's 39000-char MAX_MESSAGE_LENGTH, text-only Slack still hits chat.postMessage, and the warning text now lists slack.
  • Adjacent suitetests/tools/test_send_message_tool.py (97/97 pass, all preexisting Slack tests still green) and tests/gateway/test_slack*.py (228/228 pass).
  • Regression guard — with tools/send_message_tool.py reverted to 58a6171bf (current origin/main) but the new tests in place, three representative new tests fail (test_single_image_upload_uses_files_upload_v2, test_media_present_uses_send_slack_with_media, test_warning_message_lists_slack_as_supported); restoring the fix makes them pass.

Related

Fixes #17261.

Changed files

  • tests/tools/test_send_message_tool.py (modified, +424/-0)
  • tools/send_message_tool.py (modified, +119/-5)

Code Example

N/A
Should be easy to replicate. Will provide if requested

---
RAW_BUFFERClick to expand / collapse

Bug Description

When asked to send a file (e.g. a markdown file) as attachment, if Hermes replies in thread, everything works.

But, if you ask Hermes to send the file as a separate DM, or send to another channel, it will reply: MEDIA attachments were omitted for slack; native send_message media delivery is currently only supported for telegram, discord, matrix, weixin, and signal

Steps to Reproduce

  1. ask Hermes to generate a random markdown file
  2. ask Hermes to send the file to you as an attachment
  3. Hermes should reply in thread with attached file
  4. ask Hermes to send the file as attachment to another channel

Expected Behavior

Since Hermes is clearly able to send attachment through Slack, it should send attachment to another channel

Actual Behavior

Hermes will reply MEDIA attachments were omitted for slack; native send_message media delivery is currently only supported for telegram, discord, matrix, weixin, and signal

Affected Component

Gateway (Telegram/Discord/Slack/WhatsApp)

Messaging Platform (if gateway-related)

Slack

Debug Report

N/A
Should be easy to replicate. Will provide if requested

Operating System

Ubuntu 24.04

Python Version

3.11.15

Hermes Version

0.11.0

Additional Logs / Traceback (optional)

Root Cause Analysis (optional)

In-thread replies use the adapter's full media pipeline. When the user DMs Hermes and Hermes replies inline, the gateway runs BasePlatformAdapter._process_message_background (gateway/platforms/base.py:2415-2549). That code:

  1. Calls self.extract_media(response) to find MEDIA:<path> tags.
  2. For each file, dispatches by extension to the adapter's own send_voice / send_video / send_image_file / send_document methods.

The SlackAdapter implements all of these (gateway/platforms/slack.py:1024-1247), and they all funnel into _upload_file (slack.py:744), which calls files_upload_v2. So media uploads work in this path.

The cross-channel send_message tool uses a parallel, hand-rolled REST helper. tools/send_message_tool.py:_send_to_platform does NOT use the adapter at all for Slack. It has explicit if platform == TELEGRAM/DISCORD/MATRIX/SIGNAL/WEIXIN branches (lines 487-557) that do call adapter media methods, but Slack falls all the way through to the generic "Non-media platforms" block at line 559. There:

  • Line 568 emits the "MEDIA attachments were omitted for slack" warning.
  • Line 577 calls _send_slack, which only POSTs to chat.postMessage (send_message_tool.py:976-986) — a JSON-only endpoint that ignores attachments by design.

Proposed Fix (optional)

A if platform == Platform.SLACK and media_files: block right after the Discord block (around line 524), that routes through the adapter's send_image_file / send_voice / send_video / send_document methods — same shape as the Matrix/Feishu adapter-helper pattern at _send_matrix_via_adapter (line 1232) and _send_feishu (line 1421).

The Slack adapter is mostly stateless for uploads (files_upload_v2 just needs a bot token + chat_id + file path), so a thin _send_slack_via_adapter helper that does SlackAdapter.new(SlackAdapter) (mirroring how format_message is invoked at line 462) plus a minimal client setup should be sufficient.

Are you willing to submit a PR for this?

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

extent analysis

TL;DR

The issue can be fixed by adding a conditional block to route Slack media uploads through the adapter's media methods in the send_message_tool.py file.

Guidance

  • The root cause of the issue is that the send_message_tool.py file does not use the Slack adapter for media uploads when sending messages to another channel.
  • To fix this, add a conditional block to check if the platform is Slack and if there are media files, and then route the upload through the adapter's media methods.
  • The proposed fix suggests adding an if platform == Platform.SLACK and media_files: block after the Discord block in send_message_tool.py.
  • This block should call the Slack adapter's send_image_file, send_voice, send_video, or send_document methods to upload the media files.

Example

if platform == Platform.SLACK and media_files:
    # Create a new Slack adapter instance
    slack_adapter = SlackAdapter()
    # Upload media files using the adapter's methods
    for file in media_files:
        if file.endswith('.jpg') or file.endswith('.png'):
            slack_adapter.send_image_file(file)
        elif file.endswith('.mp3') or file.endswith('.wav'):
            slack_adapter.send_voice(file)
        elif file.endswith('.mp4') or file.endswith('.avi'):
            slack_adapter.send_video(file)
        else:
            slack_adapter.send_document(file)

Notes

  • The proposed fix assumes that the Slack adapter's media methods are correctly implemented and can handle the media file uploads.
  • The fix may require additional error handling and logging to ensure that media uploads are successful.

Recommendation

Apply the proposed workaround by adding the conditional block to send_message_tool.py to route Slack media uploads through the adapter's media methods. This should fix the issue and allow Hermes to send media attachments to another channel on Slack.

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 - ✅(Solved) Fix [Bug]: Hermes can send attachment (media file) when replying in thread, but not as a direct message in channel or DM [1 pull requests, 1 participants]