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

Discord.js V14机器人移动频道成员报错问题排查

解决Discord.js v14移动语音频道成员的TypeError问题

问题描述

从Discord.js v12升级到v14.7.1(Node.js版本18.12.1)后,实现频道成员移动功能的代码出现报错:

TypeError: Cannot read properties of undefined (reading 'setChannel')
TypeError: Cannot read properties of undefined (reading 'channelId')

原v12代码可正常运行,v14版本的代码执行时触发上述错误。

相关代码

main.js

const { Client, Events, GatewayIntentBits, Collection, ActivityType } = require("discord.js");
const fs = require("node:fs");
const path = require("node:path");
const { token } = require("./config.json");

const botIntents = [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.DirectMessages,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent,
    GatewayIntentBits.GuildMembers,
    GatewayIntentBits.GuildPresences,
    GatewayIntentBits.GuildVoiceStates
];

const client = new Client({ intents: botIntents, partials: ["CHANNEL"] });

client.commands = new Collection();

const commandsPath = path.join(__dirname, "commands");
const commandFiles = fs.readdirSync(commandsPath).filter((file) => file.endsWith(".js"));

for (const file of commandFiles) {
    const filePath = path.join(commandsPath, file);
    const command = require(filePath);
    if ("data" in command && "execute" in command) {
        client.commands.set(command.data.name, command);
    } else {
        console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
    }
}
// Bot ready event
client.once(Events.ClientReady, (c) => {
    console.log(`Logged in as ${c.user.tag}`);
    c.user.setActivity("/help", { type: ActivityType.Watching });
});
// Message input to command
client.on(Events.InteractionCreate, async (interaction) => {
    if (!(interaction.isChatInputCommand())) return;

    const command = interaction.client.commands.get(interaction.commandName);

    if (!(command)) {
        console.error(`No command matching ${interaction.commandName} was found.`);
        return;
    }

    try {
        await command.execute(interaction);
    } catch (error) {
        console.error(error);
        await interaction.reply({ content: "There was an error while executing this command!", ephemeral: true });
    }
});

client.login(token);

v12可用代码

module.exports = {
    name: "my_name",
    description: "my_description",
    execute(message, args) {
        const member = message.mentions.members.first();
        if (!(args[1])) {
            const embed = new Discord.MessageEmbed()
                .setTitle("my_command")
                .setColor(0x992d22)
                .setDescription("my_description")
                .setFooter(`Requested by ${message.author.tag}.`, message.author.displayAvatarURL);
            message.channel.send(embed);
        }
        if (!member) return message.reply("enter a member name.")
        if (!member.voice.channel) return message.reply("the member is not in a voice channel.");
        if (!message.member.voice.channel) return message.reply("you need to join a voice channel first.");
        message.channel.send("my_message");
        member.voice.setChannel("my_channel_id");
    }
}

v14尝试代码

module.exports = {
    data: new SlashCommandBuilder()
        .setName("my_name")
        .setDescription("my_description")
        .addUserOption((option) =>
            option
                .setName("member")
                .setDescription("my_description")
                .setRequired(true)
        )
        .setDMPermission(false),
    async execute(interaction) {
        const master = interaction.user.username;
        const target = interaction.options.getUser("member");
        const cageId = "my_channel_id";

        if (!(interaction.member.voice.channel)) {
            return await interaction.reply({ content: "You need to join a Voice-Channel first.", ephemeral: true });
        }
        if (!(target.voice.channelId)) {
            return await interaction.reply({ content: "The Member is currently not in a Voice-Channel.", ephemeral: true });
        }

        await target.voice.setChannel(cageId);

        const embed = new EmbedBuilder()
            .setColor(0x992d22)
            .setTitle("my_title")
            .setDescription("my_description")
            .setFooter({ text: `Requested by ${master}` })
            .setTimestamp();

        return await interaction.reply({ embeds: [embed] });
    },
}

报错信息

TypeError: Cannot read properties of undefined (reading 'setChannel')
TypeError: Cannot read properties of undefined (reading 'channelId')

问题原因与修复方案

核心问题是:Discord.js v14中,getUser()返回的是全局User对象,而语音状态(voice属性)仅存在于服务器专属的GuildMember对象中。你用getUser()拿到的目标对象没有voice属性,自然会触发undefined报错。

修复步骤

  1. 替换对象获取方式:将interaction.options.getUser("member")改为interaction.options.getMember("member"),获取目标用户的GuildMember实例。
  2. 补全必要导入:v14命令中需要导入SlashCommandBuilder和EmbedBuilder,否则会报未定义错误。
  3. 调整异常处理:添加try/catch捕获移动成员时的权限或其他异常。

修复后的v14命令代码

const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');

module.exports = {
    data: new SlashCommandBuilder()
        .setName("my_name")
        .setDescription("my_description")
        .addUserOption((option) =>
            option
                .setName("member")
                .setDescription("my_description")
                .setRequired(true)
        )
        .setDMPermission(false),
    async execute(interaction) {
        const master = interaction.user.username;
        // 获取GuildMember对象而非User对象
        const target = interaction.options.getMember("member");
        const cageId = "my_channel_id";

        if (!interaction.member.voice.channel) {
            return await interaction.reply({ content: "你需要先加入一个语音频道。", ephemeral: true });
        }
        // 先判断target.voice是否存在,再检查channelId
        if (!target.voice || !target.voice.channelId) {
            return await interaction.reply({ content: "该成员当前不在语音频道中。", ephemeral: true });
        }

        try {
            // 移动成员到目标频道
            await target.voice.setChannel(cageId);
        } catch (err) {
            console.error(err);
            return await interaction.reply({ content: "移动成员失败,请检查机器人是否拥有「移动成员」权限。", ephemeral: true });
        }

        const embed = new EmbedBuilder()
            .setColor(0x992d22)
            .setTitle("my_title")
            .setDescription("my_description")
            .setFooter({ text: `Requested by ${master}` })
            .setTimestamp();

        return await interaction.reply({ embeds: [embed] });
    },
}

额外注意事项

  • 确保机器人在目标服务器中拥有移动成员的权限(服务器设置→角色→机器人角色→开启「移动成员」权限)。
  • 你的main.js已经正确启用了GuildVoiceStates意图,无需修改,这是获取语音状态的必要条件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 13:50:48