Bug Description
When Discord auto-threading is enabled, Hermes silently falls back to answering inline in the parent channel if auto-thread creation fails. For users who rely on Discord threads as the conversation/session boundary, this is worse than a visible failure: a new task unexpectedly lands in the shared channel and breaks the channel organization model.
This happened in a normal Discord server channel with auto-threading enabled. The user sent a top-level channel message. Hermes attempted to create a thread, Discord thread creation failed because the adapter could not connect to discord.com:443, and Hermes then continued processing the message and replied directly in the parent channel.
This issue is distinct from the free_response_channels / auto_thread configuration semantics discussed in #12750 and #15262. In this case, the config should have resulted in a thread, and the log shows an auto-thread creation failure.
Steps to Reproduce
- Configure Discord with auto-threading enabled:
discord:
require_mention: false
free_response_channels: ''
allowed_channels: ''
auto_thread: true
- Send a top-level message in a Discord server channel where Hermes is allowed to respond.
- Cause
message.create_thread(...) and the fallback seed_msg.create_thread(...) path to fail, for example through a temporary Discord API / network failure.
- Observe that Hermes continues to run the agent and sends the final response inline in the parent channel.
Expected Behavior
If discord.auto_thread: true and the incoming top-level server-channel message is supposed to be routed to a newly created thread, failure to create that thread should be treated as a routing failure, not as permission to silently answer inline.
Preferred behavior:
- Retry auto-thread creation a small number of times for transient Discord/network errors.
- If thread creation still fails, do not invoke the agent for that user message.
- Send a short visible error in the parent channel, for example:
Hermes could not create a Discord thread for this message, so the request was not processed. Please retry.
This could be configurable, but the current silent inline fallback is surprising for thread-first Discord workflows.
Actual Behavior
Hermes logs the auto-thread failure, then proceeds to process the message and replies directly in the parent channel.
Example log:
2026-05-05 21:25:37,979 WARNING gateway.platforms.discord: [Discord] Auto-thread creation failed. Direct error: Cannot connect to host discord.com:443 ssl:default [None]. Fallback error: Cannot connect to host discord.com:443 ssl:default [None]
A later user message in a newly created thread worked again, which suggests the failure was transient. The confusing part is the intermediate inline response.
Affected Component
Gateway (Telegram/Discord/Slack/WhatsApp)
Messaging Platform (if gateway-related)
Discord
Debug Report
Not uploaded. Relevant local details:
Hermes Agent v0.12.0 (2026.4.30)
Project: /home/hansong/.hermes/hermes-agent
Python: 3.11.15
OpenAI SDK: 2.32.0
Current git commit: b816fd4e2
Operating System
Linux hermes 6.18.25-x64v3-xanmod1 x86_64 GNU/Linux
Python Version
Hermes runtime: Python 3.11.15
System python3: Python 3.13.5
Hermes Version
Hermes Agent v0.12.0 (2026.4.30), commit b816fd4e2
Additional Logs / Traceback (optional)
2026-05-05 21:25:37,979 WARNING gateway.platforms.discord: [Discord] Auto-thread creation failed. Direct error: Cannot connect to host discord.com:443 ssl:default [None]. Fallback error: Cannot connect to host discord.com:443 ssl:default [None]
Root Cause Analysis (optional)
In gateway/platforms/discord.py, _auto_create_thread() returns None after both direct thread creation and fallback seed-message thread creation fail:
async def _auto_create_thread(self, message):
try:
thread = await message.create_thread(...)
return thread
except Exception as direct_error:
try:
seed_msg = await message.channel.send(...)
thread = await seed_msg.create_thread(...)
return thread
except Exception as fallback_error:
logger.warning(...)
return None
Then _handle_message() only switches routing when a thread object exists:
thread = await self._auto_create_thread(message)
if thread:
parent_channel_id = str(message.channel.id)
is_thread = True
thread_id = str(thread.id)
auto_threaded_channel = thread
If thread is None, processing continues with effective_channel = auto_threaded_channel or message.channel, so the message is routed to the parent channel and the agent response appears inline.
That makes auto-thread creation best-effort rather than a routing requirement.
Proposed Fix (optional)
Add a fail-closed path when auto-threading is enabled and applicable.
Possible design:
- Let
_auto_create_thread() return a structured result instead of only thread | None, e.g. {thread, error}.
- In
_handle_message(), when auto-threading was required for a top-level server-channel message and thread creation failed, send a short error to the parent channel and return before constructing / processing the MessageEvent.
- Optionally add a config flag for backwards compatibility, for example:
discord:
auto_thread_failure_mode: error # error | inline
Defaulting to error would match thread-first workflows better; defaulting to inline would preserve current behavior. Even if the default stays inline, a documented fail-closed option would be useful.
Retrying transient failures before giving up would also help:
- retry
message.create_thread(...) 2 times with short backoff
- if all attempts fail, send the visible error and do not call the agent
Are you willing to submit a PR for this?
Bug Description
When Discord auto-threading is enabled, Hermes silently falls back to answering inline in the parent channel if auto-thread creation fails. For users who rely on Discord threads as the conversation/session boundary, this is worse than a visible failure: a new task unexpectedly lands in the shared channel and breaks the channel organization model.
This happened in a normal Discord server channel with auto-threading enabled. The user sent a top-level channel message. Hermes attempted to create a thread, Discord thread creation failed because the adapter could not connect to
discord.com:443, and Hermes then continued processing the message and replied directly in the parent channel.This issue is distinct from the
free_response_channels/auto_threadconfiguration semantics discussed in #12750 and #15262. In this case, the config should have resulted in a thread, and the log shows an auto-thread creation failure.Steps to Reproduce
message.create_thread(...)and the fallbackseed_msg.create_thread(...)path to fail, for example through a temporary Discord API / network failure.Expected Behavior
If
discord.auto_thread: trueand the incoming top-level server-channel message is supposed to be routed to a newly created thread, failure to create that thread should be treated as a routing failure, not as permission to silently answer inline.Preferred behavior:
Hermes could not create a Discord thread for this message, so the request was not processed. Please retry.This could be configurable, but the current silent inline fallback is surprising for thread-first Discord workflows.
Actual Behavior
Hermes logs the auto-thread failure, then proceeds to process the message and replies directly in the parent channel.
Example log:
A later user message in a newly created thread worked again, which suggests the failure was transient. The confusing part is the intermediate inline response.
Affected Component
Gateway (Telegram/Discord/Slack/WhatsApp)
Messaging Platform (if gateway-related)
Discord
Debug Report
Not uploaded. Relevant local details:
Operating System
Linux hermes 6.18.25-x64v3-xanmod1 x86_64 GNU/Linux
Python Version
Hermes runtime: Python 3.11.15
System
python3: Python 3.13.5Hermes Version
Hermes Agent v0.12.0 (2026.4.30), commit
b816fd4e2Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
In
gateway/platforms/discord.py,_auto_create_thread()returnsNoneafter both direct thread creation and fallback seed-message thread creation fail:Then
_handle_message()only switches routing when a thread object exists:If
threadisNone, processing continues witheffective_channel = auto_threaded_channel or message.channel, so the message is routed to the parent channel and the agent response appears inline.That makes auto-thread creation best-effort rather than a routing requirement.
Proposed Fix (optional)
Add a fail-closed path when auto-threading is enabled and applicable.
Possible design:
_auto_create_thread()return a structured result instead of onlythread | None, e.g.{thread, error}._handle_message(), when auto-threading was required for a top-level server-channel message and thread creation failed, send a short error to the parent channel and return before constructing / processing theMessageEvent.Defaulting to
errorwould match thread-first workflows better; defaulting toinlinewould preserve current behavior. Even if the default stays inline, a documented fail-closed option would be useful.Retrying transient failures before giving up would also help:
message.create_thread(...)2 times with short backoffAre you willing to submit a PR for this?