pythonerror-handlingdiscorddiscord.pypython-asyncio

discord.py app command error handling won't work because responses take >5 seconds


I'm having this issue where before a few days ago, everything worked fine, but suddenly any error handling will take too long to be in the 3 second time frame for the response to be handled. Nothing will happen for about 5 seconds after the error occurs and then an error happens because the interaction timed out which my code tries to interact with. Also for some reason in the console output both the KeyError (which actually gets caught) and the CheckFailure get listed, which doesn't make any sense normally. When looking for answers I found that someone had a similar issue and that it has to do how discord.py works with asyncio. Not sure about this though.

running.py

import discord
from discord import app_commands, Interaction
from discord.ext import commands
from discord.ext.commands import Context, CommandError

import os
import traceback

from bot import bot
from utils import errors
from utils.constants import SECRET_DB


if __name__ == '__main__':
    raise RuntimeError

TOKEN = SECRET_DB.load()['DISCORD_TOKEN']
COMMANDS_PACKAGE = 'cogs'

@bot.event
async def on_ready():
    try:
        for file_name in os.listdir(COMMANDS_PACKAGE):
            file_name = file_name.removesuffix('.py')

            if not file_name.startswith('__'):
                await bot.load_extension(f"{COMMANDS_PACKAGE}.{file_name}")
    except Exception as e:
        traceback.print_exc()
        exit(1)

    await bot.tree.sync()
    await bot.wait_until_ready()


@bot.tree.error
async def on_app_command_error(interaction: Interaction, error: app_commands.AppCommandError) -> None:
    from datetime import datetime
    print("error handling", datetime.now())

    if isinstance(error, app_commands.CommandInvokeError):
        e = error.original
    else:
        e = error

    if not interaction.response.is_done():
        await interaction.response.defer()

    if isinstance(e, errors.UserReturnableError):
        await interaction.followup.send(str(e), **e.msg_kwargs)
        return

    await interaction.followup.send("Unhandled Exception!")
    raise e


bot.run(TOKEN)

trickjump.py

import discord
from discord import app_commands, Interaction
from discord.ext import commands
from discord.ext.commands import Bot, Cog
import validators

from typing import Optional
import textwrap

from utils import retrieve, usertools
from utils.constants import *
from utils.errors import FeedbackErrors
from utils.filtered import filtered
from utils.trickjump import Base, Trickjump


class Trickjump_(Cog):
    def __init__(self, bot: Bot):
        self.bot = bot


    trickjump_group = app_commands.Group(name='trickjump', description="Command group to manage trickjumps of users")
    
    @trickjump_group.command(name="give", description="Give a jump to a user")
    async def give(self, interaction: Interaction, jump_name: str, proof: Optional[str] = None, user: Optional[str] = None):
        from datetime import datetime
        print("before except", datetime.now())
        try:
            jump = JUMPS.get()[jump_name]
        except KeyError:
            print("except clause", datetime.now())
            raise app_commands.CheckFailure

        jump.remove_attrs(filtered(
            lambda attr, rs: rs['for_user'] is False, ATTRIBUTES
        ))

        user_id, user_name = usertools.manage_and_get_id_name(interaction, user)

        proof = None if not proof else proof.strip()

        if proof:
            validators.url(proof)

        user_jumps = Base(USER_JUMPS_DB.load(user_id, []), strict=False)
            
        # If user already has this jump
        if jump_name in user_jumps:
            raise FeedbackErrors.JUMP_ALREADY_OBTAINED()

        user_jumps.append(jump)

        USER_JUMPS_DB.save(user_jumps, user_id)
        
        await interaction.response.send_message(f"The jump `{jump_name}` was successfully given to `{user_name}`!")


async def setup(bot: Bot):
    await bot.add_cog(Trickjump_(bot))
        

Output

before except 2024-07-23 13:22:58.977088
except clause 2024-07-23 13:23:07.315731
error handling 2024-07-23 13:23:07.315731
Task exception was never retrieved
future: <Task finished name='CommandTree-invoker' coro=<CommandTree._from_interaction.<locals>.wrapper() done, defined at C:\Users\JoniK\OneDrive\Dokumente\Schule\Off-School\Programmieren\Python\Jumpedia\Jumpedia\src\.venv\Lib\site-packages\discord\app_commands\tree.py:1149> exception=NotFound('404 Not Found (error code: 10062): Unknown interaction')>
Traceback (most recent call last):
  File "c:\Users\JoniK\OneDrive\Dokumente\Schule\Off-School\Programmieren\Python\Jumpedia\Jumpedia\src\cogs\trickjump.py", line 29, in give
    jump = JUMPS.get()[jump_name]
           ~~~~~~~~~~~^^^^^^^^^^^
  File "c:\Users\JoniK\OneDrive\Dokumente\Schule\Off-School\Programmieren\Python\Jumpedia\Jumpedia\src\utils\trickjump.py", line 160, in __getitem__
    raise KeyError(f"Key '{key}' not found.")
KeyError: "Key 'a' not found."

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\JoniK\OneDrive\Dokumente\Schule\Off-School\Programmieren\Python\Jumpedia\Jumpedia\src\.venv\Lib\site-packages\discord\app_commands\tree.py", line 1310, in _call
    await command._invoke_with_namespace(interaction, namespace)
  File "C:\Users\JoniK\OneDrive\Dokumente\Schule\Off-School\Programmieren\Python\Jumpedia\Jumpedia\src\.venv\Lib\site-packages\discord\app_commands\commands.py", line 883, in _invoke_with_namespace
    return await self._do_call(interaction, transformed_values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\JoniK\OneDrive\Dokumente\Schule\Off-School\Programmieren\Python\Jumpedia\Jumpedia\src\.venv\Lib\site-packages\discord\app_commands\commands.py", line 857, in _do_call
    return await self._callback(self.binding, interaction, **params)  # type: ignore
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\JoniK\OneDrive\Dokumente\Schule\Off-School\Programmieren\Python\Jumpedia\Jumpedia\src\cogs\trickjump.py", line 32, in give
    raise app_commands.CheckFailure
discord.app_commands.errors.CheckFailure

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\JoniK\OneDrive\Dokumente\Schule\Off-School\Programmieren\Python\Jumpedia\Jumpedia\src\.venv\Lib\site-packages\discord\app_commands\tree.py", line 1151, in wrapper
    await self._call(interaction)
  File "C:\Users\JoniK\OneDrive\Dokumente\Schule\Off-School\Programmieren\Python\Jumpedia\Jumpedia\src\.venv\Lib\site-packages\discord\app_commands\tree.py", line 1314, in _call
    await self.on_error(interaction, e)
  File "c:\Users\JoniK\OneDrive\Dokumente\Schule\Off-School\Programmieren\Python\Jumpedia\Jumpedia\src\running.py", line 47, in on_app_command_error
    await interaction.response.defer()
  File "C:\Users\JoniK\OneDrive\Dokumente\Schule\Off-School\Programmieren\Python\Jumpedia\Jumpedia\src\.venv\Lib\site-packages\discord\interactions.py", line 709, in defer
    await adapter.create_interaction_response(
  File "C:\Users\JoniK\OneDrive\Dokumente\Schule\Off-School\Programmieren\Python\Jumpedia\Jumpedia\src\.venv\Lib\site-packages\discord\webhook\async_.py", line 221, in request
    raise NotFound(response, data)
discord.errors.NotFound: 404 Not Found (error code: 10062): Unknown interaction

I am on a local machine, but I tried using multiple different networks, tried re-installing discord.py, tried multiple error types as well as multiple kind of ways to handle errors, but no results.


Solution

  • A bit late, but I want to follow up how I managed to solve my issue. After testing around a lot after posting this, I actually managed to figure out what the issue was.

    I actually made my own memoize decorator for a command, which would use a frozendict (from 3rd party library) on a dictionary I only realized after was about 10k items in size, which made just the conversion from dict -> frozendict take so long and therefore basically halt the entire program.

    If somebody has the same issue in the future, for testing purposes try removing commands one by one and see if the problem lies somewhere in your actual code by other debug methods, because the discord.py library actually worked as intended in my case and I just had to find a very odd bug in my code.