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

Discord反应角色按钮报错:interaction.deferUpdate()非函数或交互失败

问题描述

我正在开发带按钮的反应角色消息,把角色处理逻辑放在单独文件里,确保重启后仍能运行,但遇到两个问题:

  • 报错:interaction.deferUpdate() is not a function
  • Discord端显示「此交互失败」,但角色已成功增减

创建消息的代码

const { ApplicationCommandType, ActionRowBuilder, ButtonBuilder, EmbedBuilder } = require('discord.js');

module.exports = {
    name: 'role',
    description: "reactionroles",
    cooldown: 3000,
    userPerms: ['Administrator'],
    botPerms: ['Administrator'],
    run: async (client, message, args) => {

        const getButtons = (toggle = false, choice) => {
            const row = new ActionRowBuilder().addComponents(
                    new ButtonBuilder()
                        .setLabel('member')
                        .setCustomId('member')
                        .setStyle(toggle == true && choice == 'blue' ? 'Secondary' : 'Primary')
                        .setDisabled(toggle),
                    new ButtonBuilder()
                        .setLabel('member2')
                        .setCustomId('member2')
                        .setStyle(toggle == true && choice == 'blue' ? 'Secondary' : 'Primary')
                        .setDisabled(toggle),
            );

            return row;
        }

        const embed = new EmbedBuilder()
        .setTitle('Wähle eine rolle')
        .setDescription('Wähle die rolle, die du gern haben möchtest')
        .setColor('Aqua')

        message.channel.send({ embeds: [embed], components: [getButtons()] })
        .then((m) => {
            const collector = m.createMessageComponentCollector();
            collector.on('collect', async (i) => {
                if (!i.isButton()) return;
                await i.deferUpdate();
                
                
            });

        });
    }
};

角色处理代码

const fs = require('fs');
const chalk = require('chalk')
var AsciiTable = require('ascii-table')
var table = new AsciiTable()
const discord = require("discord.js");
table.setHeading('Events', 'Stats').setBorder('|', '=', "0", "0")

module.exports = (client) => {
    

    client.ws.on("INTERACTION_CREATE", async (i) => {
        
        let guild = client.guilds.cache.get('934096845762879508');
        let member = await guild.members.fetch(i.member.user.id)
        let role = guild.roles.cache.find(role => role.name === i.data.custom_id);
        if (!member.roles.cache.has(role.id)) {
            member.roles.add(role);
        } else {
            member.roles.remove(role);
        }
        return i.deferUpdate()
    })
};

问题原因及修复方案

1. interaction.deferUpdate() is not a function 报错原因

你用client.ws.on("INTERACTION_CREATE")拿到的i是Discord底层WebSocket原始数据对象,不是discord.js封装的标准Interaction实例,自然没有deferUpdate()这类封装方法。

2. 「此交互失败」的原因

  • 你同时用了两种交互处理逻辑:消息组件收集器(createMessageComponentCollector)和WebSocket事件,导致交互被重复触发,其中一个处理流程没有正确响应Discord的交互回调。
  • WebSocket事件里的角色操作没加await异步等待,且调用了不存在的方法,导致Discord在3秒内没收到合法响应,直接判定交互失败。

修复步骤

步骤1:移除重复的交互处理

删掉创建消息代码里的收集器逻辑,因为你已经在单独文件处理交互:

const { ApplicationCommandType, ActionRowBuilder, ButtonBuilder, EmbedBuilder } = require('discord.js');

module.exports = {
    name: 'role',
    description: "reactionroles",
    cooldown: 3000,
    userPerms: ['Administrator'],
    botPerms: ['Administrator'],
    run: async (client, message, args) => {

        const getButtons = (toggle = false, choice) => {
            const row = new ActionRowBuilder().addComponents(
                    new ButtonBuilder()
                        .setLabel('member')
                        .setCustomId('member')
                        .setStyle(toggle == true && choice == 'blue' ? 'Secondary' : 'Primary')
                        .setDisabled(toggle),
                    new ButtonBuilder()
                        .setLabel('member2')
                        .setCustomId('member2')
                        .setStyle(toggle == true && choice == 'blue' ? 'Secondary' : 'Primary')
                        .setDisabled(toggle),
            );

            return row;
        }

        const embed = new EmbedBuilder()
        .setTitle('Wähle eine rolle')
        .setDescription('Wähle die rolle, die du gern haben möchtest')
        .setColor('Aqua')

        // 直接发送消息,移除收集器
        await message.channel.send({ embeds: [embed], components: [getButtons()] });
    }
};

步骤2:改用discord.js官方interactionCreate事件处理交互

放弃底层WebSocket事件,用discord.js封装的事件,拿到标准交互实例:

const { Events } = require('discord.js');

module.exports = (client) => {
    client.on(Events.InteractionCreate, async (interaction) => {
        // 只处理按钮交互
        if (!interaction.isButton()) return;
        // 限定仅目标服务器生效
        if (interaction.guildId !== '934096845762879508') return;

        try {
            // 先延迟更新,告知Discord已收到交互(必须在3秒内调用)
            await interaction.deferUpdate();

            const role = interaction.guild.roles.cache.find(r => r.name === interaction.customId);
            if (!role) return;

            const member = interaction.member;
            if (member.roles.cache.has(role.id)) {
                await member.roles.remove(role);
            } else {
                await member.roles.add(role);
            }
        } catch (error) {
            console.error('角色处理出错:', error);
            // 出错时给用户私密提示(可选)
            if (!interaction.replied && !interaction.deferred) {
                await interaction.reply({ content: '处理角色时出现错误', ephemeral: true });
            }
        }
    });
};

关键注意点

  • 所有异步操作(增减角色、发送响应)必须加await,确保流程顺序执行。
  • 交互触发后必须在3秒内调用deferUpdate()或reply(),否则Discord会判定交互失败。
  • 使用discord.js封装的事件和对象,避免直接操作底层WebSocket数据,减少兼容性问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 04:15:40