discordjs/discord.js · error · DiscordjsError

InteractionAlreadyReplied

InteractionAlreadyReplied

Error message

The reply to this interaction has already been sent or deferred.

What it means

Interaction reply state guard: an interaction can only have one initial response. deferReply throws InteractionAlreadyReplied when this.deferred or this.replied is already true, because Discord rejects a second initial callback (interaction has already been acknowledged).

Source

Thrown at packages/discord.js/src/structures/interfaces/InteractionResponses.js:83

  /**
   * Defers the reply to this interaction.
   *
   * @param {InteractionDeferReplyOptions} [options] Options for deferring the reply to this interaction
   * @returns {Promise<InteractionCallbackResponse|undefined>}
   * @example
   * // Defer the reply to this interaction
   * interaction.deferReply()
   *   .then(console.log)
   *   .catch(console.error)
   * @example
   * // Defer to send an ephemeral reply later
   * interaction.deferReply({ flags: MessageFlags.Ephemeral })
   *   .then(console.log)
   *   .catch(console.error);
   */
  async deferReply(options = {}) {
    if (this.deferred || this.replied) throw new DiscordjsError(ErrorCodes.InteractionAlreadyReplied);

    const resolvedFlags = new MessageFlagsBitField(options.flags);

    const response = await this.client.rest.post(Routes.interactionCallback(this.id, this.token), {
      body: {
        type: InteractionResponseType.DeferredChannelMessageWithSource,
        data: {
          flags: resolvedFlags.bitfield,
        },
      },
      auth: false,
      query: makeURLSearchParams({ with_response: options.withResponse ?? false }),
    });

    this.deferred = true;
    this.ephemeral = resolvedFlags.has(MessageFlags.Ephemeral);

    return options.withResponse ? new InteractionCallbackResponse(this.client, response) : undefined;

View on GitHub (pinned to a81ed8a306)

Solutions

  1. Check interaction.deferred / interaction.replied before deferring and only defer when neither is set.
  2. If already deferred, send content with interaction.editReply() instead of deferring again.
  3. If already replied, use interaction.followUp() for additional messages.
  4. Ensure your interaction handler is registered once (guard duplicate client event listeners).

Example fix

// before
await interaction.deferReply();
await interaction.deferReply({ ephemeral: true }); // throws

// after
if (!interaction.deferred && !interaction.replied) {
  await interaction.deferReply({ flags: MessageFlags.Ephemeral });
} else {
  await interaction.editReply({ content: 'Working...' });
}
Defensive patterns

Strategy: try-catch

Validate before calling

const canDefer = !interaction.deferred && !interaction.replied;

Try / catch

try {
  await interaction.deferReply({ flags: MessageFlags.Ephemeral });
} catch (err) {
  if (err?.code === 'InteractionAlreadyReplied') {
    await interaction.editReply({ content: 'Working...' }).catch(() => {});
  } else throw err;
}

Prevention

When it happens

Trigger: Calling interaction.deferReply() after already calling reply(), deferReply(), deferUpdate(), update(), or launchActivity() on the same interaction instance — including following a defer that later auto-set replied via followUp.

Common situations: Handler runs twice due to duplicate event registration or component-collector rebinding; a code path that defers at the top and the caller defers again in an error branch; switching from reply to deferReply during refactor without removing the earlier ack.

Related errors


AI-assisted analysis of discordjs/discord.js@a81ed8a306 (2026-08-30). Data as JSON: /api/errors/e8be483159458f9a. Report an issue: GitHub.