Discord.js v14模态框报错:Cannot read properties of undefined (reading 'getTextInputValue')
问题分析与修复方案
错误原因
你写的代码在按钮交互的回调里,弹出模态框后直接调用interaction.fields.getTextInputValue("reason")——这时候用户还没提交模态框,当前的按钮交互对象里根本没有fields属性,自然会报“Cannot read properties of undefined”的错误。模态框的提交是一个独立的交互事件,和按钮点击不是同一个interaction实例,必须单独监听处理。
修复步骤
- 拆分逻辑:按钮点击只负责弹出模态框,不处理后续的输入值读取和嵌入更新
- 新增模态框提交的处理分支,监听
isModalSubmit()类型的交互 - 在模态框提交回调里,获取输入值、找到对应建议消息、更新嵌入内容
修复后的完整代码
const { PermissionFlagsBits, EmbedBuilder, TextInputStyle, TextInputBuilder, ModalBuilder, ActionRowBuilder } = require("discord.js"); const suggestionSchema = require("../../Models/Suggestion"); module.exports = { name: "interactionCreate", async execute(interaction) { const { member, guildId, customId, message } = interaction; // 处理按钮交互:弹出模态框 if (interaction.isButton()) { if (customId === "suggest-accept" || customId === "suggest-decline") { if (!member.permissions.has(PermissionFlagsBits.Administrator)) return interaction.reply({ content: "No permissions!", ephemeral: true }); // 先验证数据存在 const data = await suggestionSchema.findOne({ GuildID: guildId, MessageID: message.id }); if (!data) return interaction.reply({ content: "No data was found.", ephemeral: true }); const embed = message.embeds[0]; if (!embed) return interaction.reply({ content: "No embed was found.", ephemeral: true }); if (customId === "suggest-accept") { // 构建并弹出模态框,把消息ID放进模态框ID方便后续关联 const modal = new ModalBuilder() .setTitle('Accept Suggestion') .setCustomId(`accept-modal-${message.id}`); const reasonInput = new TextInputBuilder() .setCustomId('reason') .setRequired(true) .setLabel('Accept Reason') .setStyle(TextInputStyle.Short); const actionRow = new ActionRowBuilder().addComponents(reasonInput); modal.addComponents(actionRow); await interaction.showModal(modal); } else if (customId === "suggest-decline") { // 拒绝逻辑直接处理 embed.data.fields[2] = { name: "Status", value: "Declined", inline: true }; const declinedEmbed = EmbedBuilder.from(embed).setColor("Red"); await message.edit({ embeds: [declinedEmbed] }); await interaction.reply({ content: "Suggestion declined.", ephemeral: true }); } } return; } // 处理模态框提交交互 if (interaction.isModalSubmit()) { // 匹配接受建议的模态框ID if (customId.startsWith("accept-modal-")) { if (!member.permissions.has(PermissionFlagsBits.Administrator)) return interaction.reply({ content: "No permissions!", ephemeral: true }); // 从模态框ID里提取原建议消息的ID const messageId = customId.split("-")[2]; const targetMessage = await interaction.channel.messages.fetch(messageId).catch(() => null); if (!targetMessage) return interaction.reply({ content: "Original suggestion message not found.", ephemeral: true }); // 获取用户输入的理由 const reason = interaction.fields.getTextInputValue("reason"); // 更新嵌入内容 const embed = targetMessage.embeds[0]; embed.data.fields[2] = { name: "Status", value: "Accepted", inline: true }; embed.data.fields[3] = { name: "Reason", value: reason, inline: true }; const acceptedEmbed = EmbedBuilder.from(embed).setColor("Green"); await targetMessage.edit({ embeds: [acceptedEmbed] }); await interaction.reply({ content: "Suggestion accepted successfully.", ephemeral: true }); // 可选:更新数据库里的状态和理由 await suggestionSchema.findOneAndUpdate( { GuildID: guildId, MessageID: messageId }, { Status: "Accepted", Reason: reason } ); } } } };
关键改动说明
- 把按钮交互和模态框提交交互分开处理,各自对应
isButton()和isModalSubmit()类型判断 - 模态框的自定义ID带上了原建议消息的ID,提交时能准确找到要更新的目标消息
- 仅在模态框提交回调里读取
interaction.fields,此时用户已完成提交,fields属性有效 - 用
await处理所有异步操作,避免回调嵌套和异步顺序问题 - 新增数据库更新的可选逻辑,确保建议数据状态一致
内容的提问来源于stack exchange,提问作者Drif
相关产品推荐
相关产品推荐

