Discord.js调用栈溢出错误:/ping命令执行异常求助
问题:执行Discord
/ping 命令触发调用栈溢出错误 执行/ping命令时出现以下报错:
C:\Users\Erenay\Desktop\bot\node_modules\discord.js\src\util\Util.js:21
.filter(key => !key.startsWith('_'))
^RangeError: Maximum call stack size exceeded
at Array.filter ()
at flatten (C:\Users\Erenay\Desktop\bot\node_modules\discord.js\src\util\Util.js:21:6)
at flatten (C:\Users\Erenay\Desktop\bot\node_modules\discord.js\src\util\Util.js:48:58)
...(flatten函数重复调用,直至栈溢出)
原因分析
- Canvas对象循环引用:
chart.js生成的Chart实例会绑定到Canvas上下文上,你直接导出全局Canvas对象给ping.js使用。Discord.js序列化AttachmentBuilder时会递归遍历Canvas属性,触发循环引用,导致调用栈溢出。 - 数据库数据处理异常:当数据库中无"Ping"数据时,默认值设为
{},后续访问uptimeLinks[0].label会读取undefined的属性,虽然不是当前报错直接原因,但会导致图表初始化失败,引发连锁问题。 - 异步操作未正确等待:
wait(5000)未加await,会在图表文件写入完成前就执行删除操作,同时也可能干扰Discord.js的响应流程。
解决方案
1. 避免导出全局Canvas对象
修改/commands/ping.js,直接读取生成的本地文件作为附件,不再使用Canvas流:
const { EmbedBuilder, SlashCommandBuilder, AttachmentBuilder } = require('discord.js') const { updatePingChart } = require("../helpers/chart"); // 移除canvas导入 function wait(time) { return new Promise(resolve => setTimeout(resolve, time)); } const fs = require("fs") module.exports = { slash: true, cooldown: 1200, data: new SlashCommandBuilder() .setName('ping') .setDescription('Shows bot ping.'), async execute(client, interaction) { let pinge = '<:Bot:1065675104027148338>' let bote = '<:Ping:1065675122683412481>' let messageping = new Date().getTime() - interaction.createdTimestamp let ping = client.ws.ping // client.ws.ping不是异步操作,无需await await updatePingChart(ping) // 直接读取生成的本地文件 const attachment = new AttachmentBuilder('pingChart.png', { name: 'ping.png' }); const embed = new EmbedBuilder() .addFields({ name: `${bote} **Bot ping:**`, value: `\`${ping}ms\`` }, { name: `${pinge} **Message ping:**`, value: `\`${messageping}ms\`` }) .setColor(`Gold`) await interaction.reply({ embeds: [embed], attachments: [attachment] }) await wait(5000) // 添加await,等待文件写入完成再删除 fs.unlinkSync('pingChart.png'); } }
2. 修复数据库数据处理逻辑
修改/helpers/chart.js,确保数据格式正确,添加ping数据记录逻辑:
const db = require("croxydb") const { createCanvas } = require("canvas") const width2 = 800; const height2 = 400; const Chart = require("chart.js/auto") const fs = require("fs") let myChart; async function updatePingChart(pingValue) { if (myChart) { myChart.destroy(); } // 设默认值为空数组,而非空对象 let uptimeLinks = db.get("Ping") || []; // 初始化数据结构 if (!uptimeLinks.length) { uptimeLinks = [{ label: [], ping: [] }]; } let labels = uptimeLinks[0].label; let pinkg = uptimeLinks[0].ping; // 添加当前ping记录,限制历史数据长度(最多20条) const currentTime = new Date().toLocaleTimeString(); labels.push(currentTime); pinkg.push(pingValue); if (labels.length > 20) { labels.shift(); pinkg.shift(); } // 更新数据库 db.set("Ping", uptimeLinks); const canvas = createCanvas(width2, height2); // 每次创建新Canvas,避免全局对象污染 const ctx2 = canvas.getContext('2d'); const configuration = { type: 'line', data: { labels: labels, datasets: [{ label: 'Ping (ms)', backgroundColor: 'rgba(75, 192, 192, 0.2)', borderColor: 'rgba(75, 192, 192, 1)', data: pinkg, yAxisID: 'y-axis-1', }] }, options: { scales: { x: { // 去掉数组包裹,Chart.js scale配置无需数组 type: 'category', // 改为分类轴,适配时间标签 position: 'bottom', ticks: { callback: function(value, index) { // 调整标签显示间隔,避免拥挤 if (index % 4 === 0 && labels[index]) { return labels[index]; } else { return ''; } } } }, y: [{ type: 'linear', display: true, position: 'left', id: 'y-axis-1', }, { type: 'linear', display: true, position: 'right', id: 'y-axis-2', ticks: { max: 1, min: 0, stepSize: 1, callback: function(value) { return value === 1 ? 'Up' : 'Down'; } } }, ] } } }; myChart = new Chart(ctx2, configuration); const out = fs.createWriteStream('pingChart.png'); const stream = canvas.createPNGStream(); return new Promise((resolve, reject) => { stream.pipe(out); out.on('finish', () => { resolve('The chart image was saved.'); }); out.on('error', (err) => { reject(err); }); }); } module.exports = { updatePingChart: updatePingChart }; // 不再导出canvas对象
内容的提问来源于stack exchange,提问作者Erenay
相关产品推荐
相关产品推荐

