Node.js开发Discord机器人遇DiscordAPIError[10008]报错求助
解决Discord机器人DiscordAPIError[10008](Unknown Message)崩溃问题
问题描述
我用Node.js开发的Discord机器人,执行/register命令完成注册后,偶尔会触发DiscordAPIError[10008]错误导致崩溃。错误原因是机器人尝试删除或编辑已经被移除的消息。虽然我尝试捕获该错误代码,但问题仍未解决。
报错信息
24.06 23:05:06 [Bot] /node_modules/@discordjs/rest/dist/index.js:640 24.06 23:05:06 [Bot] throw new DiscordAPIError(data, "code" in data ? data.code : data.error, status, method, url, requestData); 24.06 23:05:06 [Bot] ^ 24.06 23:05:06 [Bot] DiscordAPIError[10008]: Unknown Message 24.06 23:05:06 [Bot] at handleErrors (/node_modules/@discordjs/rest/dist/index.js:640:13) 24.06 23:05:06 [Bot] at processTicksAndRejections (node:internal/process/task_queues:96:5) 24.06 23:05:06 [Bot] at async SequentialHandler.runRequest (/node_modules/@discordjs/rest/dist/index.js:1021:23) 24.06 23:05:06 [Bot] at async SequentialHandler.queueRequest (/node_modules/@discordjs/rest/dist/index.js:862:14) 24.06 23:05:06 [Bot] at async REST.request (/node_modules/@discordjs/rest/dist/index.js:1387:22) 24.06 23:05:06 [Bot] at async InteractionWebhook.editMessage (/node_modules/discord.js/src/structures/Webhook.js:338:15) 24.06 23:05:06 [Bot] at async ChatInputCommandInteraction.editReply (/node_modules/discord.js/src/structures/interfaces/InteractionResponses.js:158:17) { 24.06 23:05:06 [Bot] requestBody: { 24.06 23:05:06 [Bot] files: [], 24.06 23:05:06 [Bot] json: { 24.06 23:05:06 [Bot] content: 'Deleting message..', 24.06 23:05:06 [Bot] tts: false, 24.06 23:05:06 [Bot] nonce: undefined, 24.06 23:05:06 [Bot] embeds: [], 24.06 23:05:06 [Bot] components: [], 24.06 23:05:06 [Bot] username: undefined, 24.06 23:05:06 [Bot] avatar_url: undefined, 24.06 23:05:06 [Bot] allowed_mentions: undefined, 24.06 23:05:06 [Bot] flags: undefined, 24.06 23:05:06 [Bot] message_reference: undefined, 24.06 23:05:06 [Bot] attachments: undefined, 24.06 23:05:06 [Bot] sticker_ids: undefined, 24.06 23:05:06 [Bot] thread_name: undefined 24.06 23:05:06 [Bot] } 24.06 23:05:06 [Bot] }, 24.06 23:05:06 [Bot] rawError: { message: 'Unknown Message', code: 10008 }, 24.06 23:05:06 [Bot] code: 10008, 24.06 23:05:06 [Bot] status: 404, 24.06 23:05:06 [Bot] method: 'PATCH', 24.06 23:05:06 [Bot] url: 'https://discord.com/api/v10/webhooks/1121408748972408884/aW50ZXJhY3Rpb246MTEyMjMwMTAyMzc4NTA3MDYwMjpNMTc0eGVLRHFiSUEwQUlpVkFCQnN4Ukw2eExNN3JFc3d3WVk0ZzB0NUN1Unk4cFpyQVJVNmlEcDgyamVGdDByZmlpakxCaDhKODNmVm1xOTNUVFcwVW9RbWZoT2RQNzRuOHJrb3lxemtzbGtsY0pDS2FrUENXR29CbWo0VnhMVg/messages/@original' 24.06 23:05:06 [Bot] }
问题根源
原代码存在以下核心问题:
- 混合使用
async/await和.then/.catch,导致部分异步操作的错误无法被外层try/catch捕获 - 所有
interaction.editReply和interaction.deleteReply操作未单独处理错误,当消息已被用户删除或超时后,调用这些方法会触发10008错误并直接崩溃 - 数据库单次连接管理不严谨,存在潜在资源泄漏风险
修复方案
- 统一使用async/await:替换所有
.then/.catch,让所有异步错误能被统一捕获 - 封装安全交互方法:对每个编辑、删除交互操作单独捕获10008错误并忽略,避免单个操作失败导致整个程序崩溃
- 改用数据库连接池:提升性能并避免资源泄漏
- 处理特殊字符:对召唤师名称进行URL编码,避免API请求失败
修改后的完整代码
const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, SlashCommandBuilder } = require('discord.js'); const mysql = require('mysql'); const fetch = require('isomorphic-fetch'); require("dotenv").config(); // 创建数据库连接池,替代单次连接 const pool = mysql.createPool({ host: process.env.DATABASEHOST, user: process.env.DATABASEUSER, password: process.env.DATABASEPASSWORD, database: process.env.DATABASENAME, connectionLimit: 10 }); // 封装安全的回复编辑方法,自动处理10008错误 async function safeEditReply(interaction, options) { try { await interaction.editReply(options); } catch (error) { if (error.code !== 10008) { console.error('编辑回复失败:', error); } } } // 封装安全的回复删除方法,自动处理10008错误 async function safeDeleteReply(interaction, delay = 0) { if (delay > 0) { await new Promise(resolve => setTimeout(resolve, delay)); } try { await interaction.deleteReply(); } catch (error) { if (error.code !== 10008) { console.error('删除回复失败:', error); } } } module.exports = { data: new SlashCommandBuilder() .setName('register') .setDescription('Register your LoL account!') .addStringOption(option => option.setName('username') .setDescription('Your LoL username!') .setRequired(true)), async execute(interaction) { let connection; try { await interaction.reply({ content: '...', embeds: [], components: [], ephemeral: true }); const leagueUsername = interaction.options.getString('username'); // 对召唤师名称进行URL编码,处理特殊字符 const apiLink = `https://euw1.api.riotgames.com/lol/summoner/v4/summoners/by-name/${encodeURIComponent(leagueUsername)}?api_key=${process.env.LOLAPITOKEN}`; // 使用await替代then/catch,统一错误处理 const apiresponse = await fetch(apiLink); if (!apiresponse.ok) throw new Error('API request failed'); const data = await apiresponse.json(); const summonerName = data.name; const lolEmbed = new EmbedBuilder() .setColor(0x0099FF) .setDescription('To confirm this is your LoL account, change your profile picture in LoL to this picture') .setImage('http://ddragon.leagueoflegends.com/cdn/10.18.1/img/profileicon/1.png'); const discordUserID = interaction.user.id; const readyButton = new ButtonBuilder() .setCustomId('ready') .setLabel('Ready') .setStyle(ButtonStyle.Primary); const row = new ActionRowBuilder().addComponents(readyButton); // 从连接池获取连接 connection = await new Promise((resolve, reject) => { pool.getConnection((err, conn) => { if (err) reject(err); else resolve(conn); }); }); console.log('Connected to the database!'); // 查询用户是否已注册 const [results] = await new Promise((resolve, reject) => { connection.query('SELECT * FROM LoLregistration WHERE discordID = ?', [discordUserID], (err, res) => { if (err) reject(err); else resolve(res); }); }); if (results.length !== 0) { await safeEditReply(interaction, { content: 'Already registered.', embeds: [], components: [] }); await safeDeleteReply(interaction, 5000); return; } // 更新初始回复为验证界面 const response = await interaction.editReply({ content: '', embeds: [lolEmbed], components: [row], }); // 等待用户点击按钮 const collectorFilter = i => i.user.id === interaction.user.id; const confirmation = await response.awaitMessageComponent({ filter: collectorFilter, time: 90_000 }); if (confirmation.customId === 'ready') { await confirmation.update({ content: `...`, components: [] }); // 再次调用API验证头像 const apiresponse2 = await fetch(apiLink); if (!apiresponse2.ok) throw new Error('API request failed'); const data1 = await apiresponse2.json(); if (data1.profileIconId === 1) { // 插入用户数据 await new Promise((resolve, reject) => { const userData = { discordID: discordUserID, usernameLoL: summonerName }; connection.query('INSERT INTO LoLregistration SET ?', userData, (err) => { if (err) reject(err); else resolve(); }); }); console.log('Data inserted successfully!'); await safeEditReply(interaction, { content: 'Thank you for registering! :)', embeds: [], components: [] }); } else { await safeEditReply(interaction, { content: 'Incorrect profile picture.', embeds: [], components: [] }); } await safeDeleteReply(interaction, 5000); } } catch (error) { if (error.message === 'API request failed') { await safeEditReply(interaction, { content: 'No summoner found.', embeds: [], components: [] }); await safeDeleteReply(interaction, 5000); } else if (error.code === 'ETIMEDOUT') { // 处理按钮超时情况 await safeEditReply(interaction, { content: 'Deleting message..', embeds: [], components: [] }); await safeDeleteReply(interaction, 5000); } else if (error.code !== 10008) { // 只记录非10008的错误 console.error('注册流程出错:', error); await safeEditReply(interaction, { content: 'Something went wrong with registering :(', embeds: [], components: [] }); await safeDeleteReply(interaction, 5000); } } finally { // 确保数据库连接被释放回池 if (connection) { connection.release(); console.log("Connection released back to pool."); } } }, };
关键修改说明
- 安全交互方法:
safeEditReply和safeDeleteReply专门捕获10008错误,避免因消息不存在导致崩溃 - 连接池管理:替代单次连接,提升性能并确保资源正确释放
- 统一async/await:所有异步操作改用await,错误统一由外层try/catch处理
- URL编码处理:对召唤师名称编码,避免含特殊字符时API请求失败
- finally释放连接:无论流程成功或失败,数据库连接都会被正确释放
内容的提问来源于stack exchange,提问作者moosikee1
相关产品推荐
相关产品推荐

