From fc988ea46a4524c708e42fa1d7923c83ac216c29 Mon Sep 17 00:00:00 2001 From: vi Date: Fri, 31 Jul 2026 12:40:21 +0200 Subject: [PATCH 1/4] lint: resolve type ignores --- disnake/ext/commands/interaction_bot_base.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/disnake/ext/commands/interaction_bot_base.py b/disnake/ext/commands/interaction_bot_base.py index 4df2017f4d..294cf03965 100644 --- a/disnake/ext/commands/interaction_bot_base.py +++ b/disnake/ext/commands/interaction_bot_base.py @@ -1470,23 +1470,28 @@ async def process_application_commands( interaction: :class:`disnake.ApplicationCommandInteraction` The interaction to process commands for. """ + if not isinstance(self, disnake.Client): + msg = "This method is only usable in disnake.Client subclasses" + raise NotImplementedError(msg) + # This usually comes from the blind spots of the sync algorithm. # Since not all guild commands are cached, it is possible to experience such issues. # In this case, the blind spot is the interaction guild, let's fix it: if ( # if we're not currently syncing, not self._sync_queued.locked() - # and we're instructed to sync guild commands + # and we're instructed to sync guild commands, and self._command_sync_flags.sync_guild_commands - # and the current command was registered to a guild + # and we're in a guild and the current command was registered to a guild, + and interaction.guild_id and interaction.data.get("guild_id") # and we don't know the command - and not self.get_guild_command(interaction.guild_id, interaction.data.id) # pyright: ignore[reportAttributeAccessIssue] + and not self.get_guild_command(interaction.guild_id, interaction.data.id) ): # don't do anything if we aren't allowed to disable them if self._command_sync_flags.allow_command_deletion: try: - await self.bulk_overwrite_guild_commands(interaction.guild_id, []) # pyright: ignore[reportAttributeAccessIssue] + await self.bulk_overwrite_guild_commands(interaction.guild_id, []) except disnake.HTTPException: # for some reason we were unable to sync the command # either malformed API request, or some other error From 856c92d61c52779e2958d67747d43173763f58ed Mon Sep 17 00:00:00 2001 From: vi Date: Fri, 31 Jul 2026 13:02:31 +0200 Subject: [PATCH 2/4] fix: emit runtime warning instead of ephemeral response for guild app command desyncs --- disnake/ext/commands/interaction_bot_base.py | 42 ++++++-------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/disnake/ext/commands/interaction_bot_base.py b/disnake/ext/commands/interaction_bot_base.py index 294cf03965..bbe975900c 100644 --- a/disnake/ext/commands/interaction_bot_base.py +++ b/disnake/ext/commands/interaction_bot_base.py @@ -1475,8 +1475,9 @@ async def process_application_commands( raise NotImplementedError(msg) # This usually comes from the blind spots of the sync algorithm. - # Since not all guild commands are cached, it is possible to experience such issues. - # In this case, the blind spot is the interaction guild, let's fix it: + # Since not all guild commands are cached, it is possible to experience such issues + # when a guild *used to* have synced app commands, but they have been removed from the + # bot/cache, in which case the commands remain registered with the API. if ( # if we're not currently syncing, not self._sync_queued.locked() @@ -1488,34 +1489,15 @@ async def process_application_commands( # and we don't know the command and not self.get_guild_command(interaction.guild_id, interaction.data.id) ): - # don't do anything if we aren't allowed to disable them - if self._command_sync_flags.allow_command_deletion: - try: - await self.bulk_overwrite_guild_commands(interaction.guild_id, []) - except disnake.HTTPException: - # for some reason we were unable to sync the command - # either malformed API request, or some other error - # in theory this will never error: if a command exists the bot has authorisation - # in practice this is not the case, the API could change valid requests at any time - message = "This command could not be processed. Additionally, an error occurred when trying to sync commands." - else: - message = "This command has just been synced." - else: - # this block is responsible for responding to guild commands that we don't delete - # this could be changed to not respond but that behavior is undecided - message = "This command could not be processed." - try: - # This part is in a separate try-except because we still should respond to the interaction - message += ( - " More information about this: " - "https://docs.disnake.dev/page/ext/commands/additional_info.html#unknown-commands." - ) - await interaction.response.send_message( - message, - ephemeral=True, - ) - except (disnake.HTTPException, disnake.InteractionTimedOut): - pass + warnings.warn( + f"This command is not present in the bot's command cache for guild ID {interaction.guild_id}." + " This can usually happen after removing the guild from the `guild_ids` of a command," + " or from the `test_guilds` of the bot." + " To resolve this, consider clearing the guild command using" + f" `await bot.bulk_overwrite_guild_commands({interaction.guild_id}, [])`.", + SyncWarning, + stacklevel=2, + ) return command_type = interaction.data.type From 5e632a93d3f96fe3e5154184976135d0e8802114 Mon Sep 17 00:00:00 2001 From: vi Date: Fri, 31 Jul 2026 13:11:35 +0200 Subject: [PATCH 3/4] docs: update section on guild app command desync causes --- docs/ext/commands/additional_info.rst | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/ext/commands/additional_info.rst b/docs/ext/commands/additional_info.rst index e52f3d3c9c..c04a21a2a0 100644 --- a/docs/ext/commands/additional_info.rst +++ b/docs/ext/commands/additional_info.rst @@ -16,29 +16,28 @@ App command sync If you're using :ref:`disnake_ext_commands` for application commands (slash commands, context menus) you should understand how your commands show up in Discord. By default, the library registers / updates all commands automatically. -Based on the application commands defined in your code the library automatically determines -which commands should be registered, edited or deleted, but there're some edge cases you should keep in mind. +Based on the application commands defined in your code, the library automatically determines +which commands should be registered, edited or deleted, but there are some edge cases you should keep in mind. Unknown Commands -+++++++++++++++++ +++++++++++++++++ Unlike global commands, per-guild application commands are synced in a lazy fashion. This is due to Discord ratelimits, -as checking all guilds for application commands is infeasible past two or three guilds. -This can lead to situations where a command no longer exists in the code but still exists in a server. +as checking all guilds for application commands is infeasible beyond only a handful of guilds. +This can lead to situations where a command no longer exists in the code but still exists in the Discord API. -To rectify this, just run the command. It will automatically be deleted. +When an interaction for a previously removed guild command is received, a :class:`SyncWarning` will be emitted. +To rectify this, consider using :meth:`await bot.bulk_overwrite_guild_commands(\, []) `. -.. _changing-test-guilds: - -This will also occur when IDs are removed from the ``test_guilds`` kwarg of :class:`Bot ` (or a similar class) or from the ``guild_ids`` kwarg of -:func:`slash_command `, :func:`user_command `, or :func:`message_command `. +This will also occur when IDs are removed from the ``test_guilds`` kwarg of :class:`~ext.commands.Bot` or from the ``guild_ids`` kwarg of +:func:`~ext.commands.slash_command`, :func:`~ext.commands.user_command`, or :func:`~ext.commands.message_command`. Command Sync with Multiple Clusters -++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++ If your bot requires shard distribution across several clusters, you should disable command sync on all clusters except one. -This will prevent conflicts and race conditions. Discord API doesn't provide users with events related to application command updates, -so it's impossible to keep the cache of multiple machines synced. Having only 1 cluster with ``sync_commands`` set to ``True`` is enough +This will prevent conflicts and race conditions. The Discord API doesn't provide users with events related to application command updates, +so it's impossible to keep the cache of multiple machines synced. Having only 1 cluster with :attr:`CommandSyncFlags.sync_commands ` set to ``True`` is enough because global registration of application commands doesn't depend on sharding. .. _why_params_and_injections_return_any: From 46b80861baa9069c47478e25046b00d15ad887d6 Mon Sep 17 00:00:00 2001 From: vi Date: Fri, 31 Jul 2026 13:14:01 +0200 Subject: [PATCH 4/4] chore: add changelog entry --- changelog/886.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/886.misc.rst diff --git a/changelog/886.misc.rst b/changelog/886.misc.rst new file mode 100644 index 0000000000..452028967e --- /dev/null +++ b/changelog/886.misc.rst @@ -0,0 +1 @@ +Emit a :class:`SyncWarning` instead of sending an ephemeral interaction response when an unknown guild application command is invoked.