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

discord.js使用embed反应翻页时触发DiscordAPIError: Unknown Message报错

问题原因

你遇到的报错核心是全局事件监听器重复注册,具体逻辑如下:

  • 每次调用movieChoiceEmbed函数,都会通过client.on("messageReactionAdd")给客户端新增一个独立的反应事件监听器,而非覆盖之前的监听器。
  • 第一次调用函数时注册1个监听器,第一次按下反应后监听器正常执行删除消息,随后再次调用movieChoiceEmbed,此时会新增第2个监听器,总共有2个监听器同时生效。
  • 第二次按下反应时,2个监听器会同时触发:第一个监听器执行时正常删除消息,第二个监听器执行时该消息已经被删除,请求Discord接口删除不存在的消息就会抛出Unknown Message错误。
  • 更关键的是,就算await reaction.message.delete()执行报错,后面修改count、重新调用movieChoiceEmbed的逻辑依然会执行,所以每次按下反应会新增和当前监听器数量相同的新监听器,总监听器数量每次翻倍,报错数量也会同步翻倍。
  • 你尝试删除频道最新消息依然报错,也是因为多个监听器几乎同时执行,第一个监听器已经把最新的embed消息删掉了,后续监听器拿到的lastMessage要么不存在要么不是目标消息,依然会触发接口报错。
解决方案

有两种常用的修复方案:

方案1:全局仅注册一次事件监听器(也就是你排查到的解法)

把messageReactionAdd监听逻辑移到movieChoiceEmbed函数外面,全局只注册一次,避免重复叠加。需要额外做消息校验,避免误处理其他消息的反应,示例逻辑:

// 外部存储分页状态
let currentPageCount = 0;
let targetMovies = null;
let triggerMessage = null;

// 全局只注册一次监听
client.on("messageReactionAdd", async (reaction, user) => {
    if(user.bot) return;
    // 只处理分页embed的反应,可根据embed标题、消息ID等判断
    if(reaction.message.embeds[0]?.title !== 'Choose movie') return;
    if(!["⏪", "⏩"].includes(reaction.emoji.name)) return;

    await reaction.message.delete();
    if(reaction.emoji.name === "⏪") currentPageCount -=4;
    else currentPageCount +=4;
    movieChoiceEmbed(targetMovies, triggerMessage, currentPageCount);
});

// 分页函数里不再注册监听器
function movieChoiceEmbed(movies, message, count) {
    targetMovies = movies;
    triggerMessage = message;
    currentPageCount = count;
    // 原有发送embed、加反应的逻辑不变,去掉内部的client.on代码
}

方案2:使用单条消息的反应收集器(更推荐)

不需要全局监听,只给当前发送的embed消息绑定专属的反应收集器,处理完自动销毁,不会产生监听器叠加的问题,示例修改:

function movieChoiceEmbed(movies, message, count) {
    const pagecount = count;
    message.channel.send({
        embed: {
            // 原有embed配置不变,这里省略
        }
    }).then(async sentEmbed => {
        await sentEmbed.react("⏪");
        await sentEmbed.react("⏩");
        // 创建仅作用于当前这条embed的反应收集器
        const collector = sentEmbed.createReactionCollector(
            (reaction, user) => !user.bot && ["⏪", "⏩"].includes(reaction.emoji.name),
            { max: 1 } // 只收集一次反应就自动销毁
        );
        collector.on("collect", async reaction => {
            await sentEmbed.delete();
            const newCount = reaction.emoji.name === "⏪" ? count -4 : count +4;
            movieChoiceEmbed(movies, message, newCount);
        });
    }).catch(console.error);
    // 删掉原来的client.on监听代码
}
为什么把监听器移到函数外可以解决问题

把messageReactionAdd移到函数外部后,整个程序生命周期内只会注册一次事件监听器,不管你调用多少次分页函数都不会新增监听器。每次按下反应只会有一个监听器执行,不会出现多个监听器同时操作同一条已删除消息的情况,自然就不会触发报错。
但这个方案要注意做好消息过滤,否则其他消息的点击反应也可能触发分页逻辑,造成不必要的异常。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 12:09:03