From 04e8edcbdb878903faa04678a3f6a64594639914 Mon Sep 17 00:00:00 2001 From: Carl Vitullo Date: Mon, 26 Jan 2026 21:39:20 -0500 Subject: [PATCH] Fix interaction timeout in Report and Track commands Defer replies immediately to avoid Discord's 3-second interaction timeout. Creating user threads can involve multiple API calls that exceed this limit, causing "Unknown interaction" errors when no thread exists yet. Co-Authored-By: Claude Opus 4.5 --- app/commands/report.ts | 10 ++++++---- app/commands/track.ts | 12 ++++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/commands/report.ts b/app/commands/report.ts index 1500d23..d06d15c 100644 --- a/app/commands/report.ts +++ b/app/commands/report.ts @@ -1,6 +1,7 @@ import { ApplicationCommandType } from "discord-api-types/v10"; import { ContextMenuCommandBuilder, + MessageFlags, PermissionFlagsBits, type MessageContextMenuCommandInteraction, } from "discord.js"; @@ -16,6 +17,9 @@ const command = new ContextMenuCommandBuilder() .setDefaultMemberPermissions(PermissionFlagsBits.SendMessages); const handler = async (interaction: MessageContextMenuCommandInteraction) => { + // Defer immediately to avoid 3-second timeout - creating threads can take time + await interaction.deferReply({ flags: [MessageFlags.Ephemeral] }); + await trackPerformance( "reportCommand", async () => { @@ -50,8 +54,7 @@ const handler = async (interaction: MessageContextMenuCommandInteraction) => { // Track command success commandStats.commandExecuted(interaction, "report", true); - await interaction.reply({ - ephemeral: true, + await interaction.editReply({ content: "This message has been reported anonymously", }); } catch (error) { @@ -69,8 +72,7 @@ const handler = async (interaction: MessageContextMenuCommandInteraction) => { // Track command failure in business analytics commandStats.commandFailed(interaction, "report", err.message); - await interaction.reply({ - ephemeral: true, + await interaction.editReply({ content: "Failed to submit report. Please try again later.", }); } diff --git a/app/commands/track.ts b/app/commands/track.ts index 6de25e7..374f947 100644 --- a/app/commands/track.ts +++ b/app/commands/track.ts @@ -5,6 +5,7 @@ import { ButtonStyle, ContextMenuCommandBuilder, InteractionType, + MessageFlags, PermissionFlagsBits, } from "discord.js"; import { Effect } from "effect"; @@ -33,6 +34,11 @@ export const Command = [ .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages), handler: (interaction) => Effect.gen(function* () { + // Defer immediately to avoid 3-second timeout - creating threads can take time + yield* Effect.tryPromise(() => + interaction.deferReply({ flags: [MessageFlags.Ephemeral] }), + ); + const { targetMessage: message, user } = interaction; if (interaction.guildId) { @@ -52,9 +58,8 @@ export const Command = [ ); yield* Effect.tryPromise(() => - interaction.reply({ + interaction.editReply({ content: `Tracked <#${thread.id}>`, - ephemeral: true, components: reportId ? [ new ActionRowBuilder().addComponents( @@ -74,9 +79,8 @@ export const Command = [ error, }); yield* Effect.tryPromise(() => - interaction.reply({ + interaction.editReply({ content: "Failed to track message", - ephemeral: true, }), ).pipe(Effect.catchAll(() => Effect.void)); }),