You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

discord.py应用命令错误处理超时,交互失效求助

问题

discord.py机器人此前运行正常,近期突然出现应用命令错误处理超时:错误触发后约5秒无响应,因交互超时导致后续操作报Unknown interaction错误。控制台同时输出捕获的KeyError和抛出的CheckFailure,疑似与discord.py和asyncio的协作机制有关。已尝试更换网络、重装discord.py,均无效。附相关代码及报错日志:

相关代码

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))

报错日志

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
分析

从日志时间戳可见,before except与except clause间隔8秒以上,远超Discord交互的3秒超时限制,直接导致后续错误处理时交互已失效,触发Unknown interaction。

核心问题是JUMPS.get()为同步阻塞操作,占用了asyncio事件循环的全部时间,命令执行被卡住,直到该操作完成才进入异常处理,此时交互早已超时。此外,错误处理中的await interaction.response.defer()属于无效操作,因为交互已超时,Discord服务器无法识别该请求。

解决方案
  1. 异步化同步阻塞操作
    将JUMPS.get()这类耗时同步操作放入线程池执行,避免阻塞事件循环:
# 修改trickjump.py中的give命令
import asyncio

@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())
    # 用线程池执行同步操作
    jumps = await asyncio.to_thread(JUMPS.get)
    try:
        jump = jumps[jump_name]
    except KeyError:
        print("except clause", datetime.now())
        # 直接返回错误响应,无需抛出CheckFailure
        await interaction.response.send_message(f"跳跃名称`{jump_name}`不存在!", ephemeral=True)
        return
    # 后续逻辑保持不变
  1. 优化错误处理逻辑
    先判断交互是否仍可响应,避免无效操作:
@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())

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

    # 先检查交互状态
    if interaction.response.is_done():
        try:
            await interaction.followup.send("操作超时,请重试!", ephemeral=True)
        except discord.errors.NotFound:
            pass
        return

    # 仅在交互有效时发送响应
    if isinstance(e, errors.UserReturnableError):
        await interaction.response.send_message(str(e), **e.msg_kwargs, ephemeral=True)
    else:
        await interaction.response.send_message("发生未处理的异常!", ephemeral=True)
  1. 修正错误类型使用
    不要用CheckFailure处理参数错误,该异常专为权限检查失败设计。直接发送错误响应或自定义参数错误类型,逻辑更清晰,避免不必要的异常传播。

内容的提问来源于stack exchange,提问作者JoniKauf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 16:23:15