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

Discord.js v14 TypeError:无法读取undefined的'commands'属性求助

Discord.js v14 重载命令时出现TypeError问题排查

错误信息

TypeError: Cannot read properties of undefined (reading 'commands')
    at loadCommands (/home/runner/tester/handlers/commandHandler.js:19:14)
    at Object.execute (/home/runner/tester/commands/owner/reload.js:33:11)
    at Client.<anonymous> (/home/runner/tester/index.js:58:17)
    at Client.emit (node:events:513:28)
    at Client.emit (node:domain:489:12)
    at InteractionCreateAction.handle (/home/runner/tester/node_modules/discord.js/src/client/actions/InteractionCreate.js:97:12)
    at module.exports [as INTERACTION_CREATE] (/home/runner/tester/node_modules/discord.js/src/client/websocket/handlers/INTERACTION_CREATE.js:4:36)
    at WebSocketManager.handlePacket (/home/runner/tester/node_modules/discord.js/src/client/websocket/WebSocketManager.js:352:31)
    at WebSocketShard.onPacket (/home/runner/tester/node_modules/discord.js/src/client/websocket/WebSocketShard.js:494:22)

涉及代码文件

commandHandler.js

function loadCommands(client) {
  const ascii = require("ascii-table");
  const fs = require("fs");
  const table = new ascii().setHeading("File Name", "Status");
  require("colors");

  let commandsArray = [];

  const commandsFolder = fs.readdirSync("./commands");
  for (const folder of commandsFolder) {
    const commandFiles = fs
      .readdirSync(`./commands/${folder}`)
      .filter((file) => file.endsWith(".js"));

    for (const file of commandFiles) {
      const commandFile = require(`../commands/${folder}/${file}`);

      const properties = { folder, ...commandFile };
      client.commands.set(commandFile.data.name, properties);

      commandsArray.push(commandFile.data.toJSON());

      table.addRow(file, "Loaded");
      continue;
    }
  }

  client.application.commands.set(commandsArray);

  return console.log(table.toString(), "\n[+]".green + " Loaded Commands");
}

module.exports = { loadCommands };

reload.js

const { Client, SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const { loadCommands } = require("../../handlers/commandHandler");
const { loadEvents } = require("../../handlers/eventHandler");

module.exports = {
  data: new SlashCommandBuilder()
  .setName('reload')
  .setDescription('Reload your commands or events.')
  .addSubcommand(subcommand =>
  subcommand.setName('commands')
  .setDescription('Reload your commands.')
  )
    .addSubcommand(subcommand =>
      subcommand.setName('events')
      .setDescription('Reload your events.')
      ),

    async execute(interaction, client) {
      const { user } = interaction;
      if (user.id !== "217414221728710656") return interaction.reply({
        embeds: [new EmbedBuilder()
        .setColor('#2f3136')
        .setDescription('This command is only for the bot developer!')], ephemeral: true
      })

      const sub = interaction.options.getSubcommand()
      const embed = new EmbedBuilder()
      .setTitle('Developer')
      .setColor('#2f3136')

      switch (sub) {
        case "commands": {
          loadCommands(client)
          interaction.reply({ embeds: [embed.setDescription('Commands reloaded!')] })
          console.log(`${user} has reloaded the bot commands.`)
        }
        break;
        case "events": {
          loadEvents(client)
          interaction.reply({ embeds: [embed.setDescription('Events reloaded!')] })
          console.log(`${user} has reloaded the bot events.`)
        }
        break;
        }
    }
}

index.js

const { Client, Collection, Events, GatewayIntentBits, Partials, ActivityType } = require('discord.js');
require("colors");

const fs = require('node:fs');
const path = require('node:path');
const { token } = require('./config.json');

const client = new Client({ intents: [GatewayIntentBits.Guilds] });

client.commands = new Collection();

const foldersPath = path.join(__dirname, 'commands');
const commandFolders = fs.readdirSync(foldersPath);

for (const folder of commandFolders) {
    const commandsPath = path.join(foldersPath, folder);
    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.`);
        }
    }
}

const eventsPath = path.join(__dirname, 'events');
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));

for (const file of eventFiles) {
    const filePath = path.join(eventsPath, file);
    const event = require(filePath);
    if (event.once) {
        client.once(event.name, (...args) => event.execute(...args));
    } else {
        client.on(event.name, (...args) => event.execute(...args));
    }
}

const { loadCommands } = require("./handlers/commandHandler.js");
const { loadEvents } = require("./handlers/eventHandler.js");

client.once(Events.ClientReady, () => {
  console.log(`--------------------------------------------------------`)
  console.log("ඞ".red + " sus")
  console.log(`--------------------------------------------------------`)
                                            
  console.log(
    `Logged in as: ${client.user.tag.green} |`, `User ID: ${client.user.id.green}`,
  );
  client.user.setActivity("Duck ASMR", {type: ActivityType.Streaming, url: "https://www.twitch.tv/dashducks" });
  console.log(`--------------------------------------------------------`)
  console.log(`Current Servers In:`)
  console.log(``)
  client.guilds.cache.forEach((guild) => {
    console.log(`  - ${guild.name.blue}`);
  });
  console.log(`--------------------------------------------------------`)
});

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);
        if (interaction.replied || interaction.deferred) {
            await interaction.followUp({ content: 'There was an error while executing this command!', ephemeral: true });
        } else {
            await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
        }
    }
});

module.expots = client;

client.login(token).then(() => {
  loadEvents(client);
  loadCommands(client);
})

const express = require('express')
const app = express();
const port = 2000;

app.get('/', (req, res) => {
  res.send('Hello World! ☀️');
})

  app.listen(port, () => {
  console.log(`- App webview active at http://localhost:${port}`)
})

问题背景

开发Discord.js v14机器人复习JavaScript,实现命令与事件重载斜杠命令时触发上述错误,搜索类似问题未找到有效方案,需要错误排查帮助及相关方法学习。

错误原因及修复方案

核心原因

loadCommands函数中访问client.commands时,传入的client参数为undefined。追踪发现,reload.js的execute函数定义需要interaction和client两个参数,但index.js中调用execute时只传了interaction,导致client参数缺失。

修复步骤

  1. 修正index.js中的命令调用
    找到InteractionCreate事件中的command.execute调用,补充传入client参数:
try {
    await command.execute(interaction, client);
} catch (error) {
  1. 修正index.js中的笔误
    文件末尾的module.expots = client;是拼写错误,改为:
module.exports = client;

(该笔误不直接导致当前错误,但可能引发其他未知问题)

  1. 添加参数合法性校验(可选)
    在commandHandler.js的loadCommands函数开头添加校验,提前拦截非法参数:
function loadCommands(client) {
  if (!client || !client.commands) {
      throw new Error("Invalid client instance provided to loadCommands");
  }
  // 原函数代码...
}

错误排查思路

  1. 定位错误位置:从错误栈中找到出错代码行(commandHandler.js:19),明确是client.commands访问失败,说明client为undefined或client.commands未初始化。
  2. 追踪参数来源:查看loadCommands的调用方(reload.js的execute函数),确认client参数来自execute的入参。
  3. 检查参数传递链:回到index.js的命令调用逻辑,发现调用execute时未传入client,导致参数缺失。
  4. 验证修复效果:修正后重新运行,测试重载命令是否正常工作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 21:03:07