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服务器无法识别该请求。
解决方案
- 异步化同步阻塞操作
将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 # 后续逻辑保持不变
- 优化错误处理逻辑
先判断交互是否仍可响应,避免无效操作:
@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)
- 修正错误类型使用
不要用CheckFailure处理参数错误,该异常专为权限检查失败设计。直接发送错误响应或自定义参数错误类型,逻辑更清晰,避免不必要的异常传播。
内容的提问来源于stack exchange,提问作者JoniKauf
相关产品推荐
相关产品推荐

