discord.js 中 component.toJSON 报错及未知行错误 [已解决]
结论
问题出在这段调用:
actionRow.addComponents(button =>
button
.setCustomId('button')
.setLabel('Open form')
.setStyle(discord.ButtonStyle.Primary)
)
ActionRowBuilder#addComponents() 接收的是已经创建好的组件 Builder。普通回调没有说明要创建 ButtonBuilder、Select Menu 还是其他组件,因此这个回调函数会被当作 component 保存。发送消息时,discord.js 会依次调用每个 component 的 toJSON()。普通函数没有这个方法,所以会报错:
TypeError: component.toJSON is not a function
解决办法是显式创建 ButtonBuilder,或者使用 addPrimaryButtonComponents() 这类已经指定按钮类型的方法。
修改方法一:显式创建 ButtonBuilder
可以先创建按钮,再把它传给 addComponents():
require('dotenv').config();
const discord = require('discord.js');
const client = new discord.Client({
intents: [discord.GatewayIntentBits.Guilds],
});
const button = new discord.ButtonBuilder()
.setCustomId('button')
.setLabel('Open form')
.setStyle(discord.ButtonStyle.Primary);
const container = new discord.ContainerBuilder()
.setAccentColor(0x0099ff)
.addActionRowComponents(actionRow =>
actionRow.addComponents(button)
);
client.once(discord.Events.ClientReady, readyClient => {
const channel = readyClient.channels.cache.get(/* channel ID */);
channel.send({
components: [container],
flags: discord.MessageFlags.IsComponentsV2,
});
});
client.login(process.env.TOKEN);
也可以直接在 addComponents() 中创建实例:
const container = new discord.ContainerBuilder()
.setAccentColor(0x0099ff)
.addActionRowComponents(actionRow =>
actionRow.addComponents(
new discord.ButtonBuilder()
.setCustomId('button')
.setLabel('Open form')
.setStyle(discord.ButtonStyle.Primary)
)
);
这样存入 Action Row 的就是实现了 toJSON() 的 ButtonBuilder,可以正常序列化。
修改方法二:使用按钮类型对应的回调方法
如果当前安装的 discord.js 版本提供 addPrimaryButtonComponents(),也可以保留回调写法:
const container = new discord.ContainerBuilder()
.setAccentColor(0x0099ff)
.addActionRowComponents(actionRow =>
actionRow.addPrimaryButtonComponents(button =>
button
.setCustomId('button')
.setLabel('Open form')
)
);
addPrimaryButtonComponents() 已经指定了按钮类型,能够把 Primary Button Builder 传给回调。按钮样式也由这个方法确定,无须再调用:
.setStyle(discord.ButtonStyle.Primary)
discord.js 各版本提供的辅助方法可能不同。如果当前版本没有 addPrimaryButtonComponents(),直接使用第一种写法创建 ButtonBuilder 即可。
为什么堆栈只指向 node_modules
Builder 在调用 addComponents() 时,通常不会立即完成所有校验。直到发送消息或调用 container.toJSON(),错误才会暴露。
序列化过程大致如下:
ContainerBuilder.toJSON()
-> ActionRowBuilder.toJSON()
-> component.toJSON()
因此,堆栈最终停在:
this.components.map(component => component.toJSON())
这行代码只是第一次真正用到错误 component 的地方,问题实际发生在此前的组件构建阶段。
调试时,可以在发送消息前主动序列化:
console.dir(container.toJSON(), { depth: null });
如果组件树有问题,错误会直接在这里出现,比从 channel.send() 的堆栈向前排查更容易缩小范围。
Invalid Array Length 和 undefined 的原因
Invalid Array Length 通常说明某个复合组件生成了空数组,或者子组件数量不符合验证规则。如果 separators、sections 或其他组件也使用了不合适的通用回调,回调可能会被保存成错误值,目标 Builder 也可能没有收到有效的子组件。
可以逐层检查组件结构:
console.dir(container.components, { depth: null });
数组中的每个元素都应该是实际的 Builder 对象,不能是函数、undefined 或空数组。排查时还可以暂时只保留一个 Action Row 和一个 Button。确认消息可以发送后,再逐个加回 Section、Separator 和 Thumbnail。
container.components[0].components[0] 不是稳定的公共调试接口。不同 Builder 保存内容的结构可能不同,回调也可能稍后才会解析。不要通过内部数组继续调用 setCustomId(),应保留显式创建的 Builder 引用:
const button = new discord.ButtonBuilder()
.setCustomId('button')
.setLabel('Open form')
.setStyle(discord.ButtonStyle.Primary);
console.log(button.toJSON());
其他注意事项
回调使用花括号时,需要显式返回 Builder:
.addActionRowComponents(actionRow => {
return actionRow.addComponents(button);
})
原示例中的箭头函数没有花括号,会隐式返回结果,所以外层回调没有问题。参数列表末尾的逗号也是合法的 JavaScript 语法,不会引发这次错误。
使用 Components V2 前,还要确认当前安装的 discord.js 版本包含 ContainerBuilder、MessageFlags.IsComponentsV2 和代码中使用的专用组件方法。不同版本的 Builder API 可能存在差异,不能直接混用其他版本文档中的回调写法。
持续时间文本和 custom thumbnail 可能只是让消息重新走到了有问题的组件分支。component.toJSON is not a function 表明 Action Row 中存在无法序列化的值,与文本内容本身没有直接关系。
备注:内容仅供参考。
