Skip to content

[Bug]: Discord auto-thread failure silently falls back to inline channel reply #20243

Description

@x-hansong

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

  1. Configure Discord with auto-threading enabled:
discord:
  require_mention: false
  free_response_channels: ''
  allowed_channels: ''
  auto_thread: true
  1. Send a top-level message in a Discord server channel where Hermes is allowed to respond.
  2. Cause message.create_thread(...) and the fallback seed_msg.create_thread(...) path to fail, for example through a temporary Discord API / network failure.
  3. 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:

  1. Let _auto_create_thread() return a structured result instead of only thread | None, e.g. {thread, error}.
  2. 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.
  3. 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?

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existscomp/gatewayGateway runner, session dispatch, deliveryplatform/discordDiscord bot adaptertype/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions