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

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错误并直接崩溃
  • 数据库单次连接管理不严谨,存在潜在资源泄漏风险

修复方案

  1. 统一使用async/await:替换所有.then/.catch,让所有异步错误能被统一捕获
  2. 封装安全交互方法:对每个编辑、删除交互操作单独捕获10008错误并忽略,避免单个操作失败导致整个程序崩溃
  3. 改用数据库连接池:提升性能并避免资源泄漏
  4. 处理特殊字符:对召唤师名称进行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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 17:37:11