From 32a694ad5f630fbe8a2f33a7f696ad238d515fa6 Mon Sep 17 00:00:00 2001 From: sgaofen <135070653+sgaofen@users.noreply.github.com> Date: Sun, 12 Apr 2026 17:31:57 -0700 Subject: [PATCH] fix(discord): fall back when auto-thread creation fails --- gateway/platforms/discord.py | 22 +++++++++++++-- tests/gateway/test_discord_slash_commands.py | 29 +++++++++++++++++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/gateway/platforms/discord.py b/gateway/platforms/discord.py index a4f33516b45..b746c7ea598 100644 --- a/gateway/platforms/discord.py +++ b/gateway/platforms/discord.py @@ -2416,9 +2416,25 @@ class DiscordAdapter(BasePlatformAdapter): try: thread = await message.create_thread(name=thread_name, auto_archive_duration=1440) return thread - except Exception as e: - logger.warning("[%s] Auto-thread creation failed: %s", self.name, e) - return None + except Exception as direct_error: + display_name = getattr(getattr(message, "author", None), "display_name", None) or "unknown user" + reason = f"Auto-threaded from mention by {display_name}" + try: + seed_msg = await message.channel.send(f"\U0001f9f5 Thread created by Hermes: **{thread_name}**") + thread = await seed_msg.create_thread( + name=thread_name, + auto_archive_duration=1440, + reason=reason, + ) + return thread + except Exception as fallback_error: + logger.warning( + "[%s] Auto-thread creation failed. Direct error: %s. Fallback error: %s", + self.name, + direct_error, + fallback_error, + ) + return None async def send_exec_approval( self, chat_id: str, command: str, session_key: str, diff --git a/tests/gateway/test_discord_slash_commands.py b/tests/gateway/test_discord_slash_commands.py index 310d518230d..2302e49ef51 100644 --- a/tests/gateway/test_discord_slash_commands.py +++ b/tests/gateway/test_discord_slash_commands.py @@ -401,6 +401,8 @@ async def test_auto_create_thread_uses_message_content_as_name(adapter): message = SimpleNamespace( content="Hello world, how are you?", create_thread=AsyncMock(return_value=thread), + channel=SimpleNamespace(send=AsyncMock()), + author=SimpleNamespace(display_name="Jezza"), ) result = await adapter._auto_create_thread(message) @@ -419,6 +421,8 @@ async def test_auto_create_thread_truncates_long_names(adapter): message = SimpleNamespace( content=long_text, create_thread=AsyncMock(return_value=thread), + channel=SimpleNamespace(send=AsyncMock()), + author=SimpleNamespace(display_name="Jezza"), ) result = await adapter._auto_create_thread(message) @@ -430,10 +434,33 @@ async def test_auto_create_thread_truncates_long_names(adapter): @pytest.mark.asyncio -async def test_auto_create_thread_returns_none_on_failure(adapter): +async def test_auto_create_thread_falls_back_to_seed_message(adapter): + thread = SimpleNamespace(id=555, name="Hello") + seed_message = SimpleNamespace(create_thread=AsyncMock(return_value=thread)) message = SimpleNamespace( content="Hello", create_thread=AsyncMock(side_effect=RuntimeError("no perms")), + channel=SimpleNamespace(send=AsyncMock(return_value=seed_message)), + author=SimpleNamespace(display_name="Jezza"), + ) + + result = await adapter._auto_create_thread(message) + assert result is thread + message.channel.send.assert_awaited_once_with("🧵 Thread created by Hermes: **Hello**") + seed_message.create_thread.assert_awaited_once_with( + name="Hello", + auto_archive_duration=1440, + reason="Auto-threaded from mention by Jezza", + ) + + +@pytest.mark.asyncio +async def test_auto_create_thread_returns_none_when_direct_and_fallback_fail(adapter): + message = SimpleNamespace( + content="Hello", + create_thread=AsyncMock(side_effect=RuntimeError("no perms")), + channel=SimpleNamespace(send=AsyncMock(side_effect=RuntimeError("send failed"))), + author=SimpleNamespace(display_name="Jezza"), ) result = await adapter._auto_create_thread(message)