Inconsistent "Unknown Interaction" Error

I checked the probably causes but neither of them should be causing the issue. - My interactions are already deferred. - I'm not using collectors. - I have reset the bot token and pasted it where I'm hosting the bot, making sure it's only started once. - I'm not deferring modals. And this happens on other interactions as well. I attached some error traces as well. I could of course send codes, but that will be a lot of code. So it would be great if someone could first give me some specific things to look for to help you help me.
No description
No description
No description
No description
No description
36 Replies
d.js toolkit
d.js toolkit2w ago
- What's your exact discord.js npm list discord.js and node node -v version? - Not a discord.js issue? Check out #other-js-ts. - Consider reading #how-to-get-help to improve your question! - Explain what exactly your issue is. - Post the full error stack trace, not just the top part! - Show your code! - Issue solved? Press the button!
materwelon
materwelonOP2w ago
Node: 23-alpine Djs: 14.18.0
Amgelo
Amgelo2w ago
do you know what actually happens in discord? does it error with application did not respond?
materwelon
materwelonOP2w ago
Hmmm, not sure. We haven't seen anyone report it yet. Let me go and check if someone opened a ticket about it though. In the meantime, here's the Ipa.ts file. (one of the errors originate here)
import { ButtonInteraction, MessageFlags } from 'discord.js';
import { InteractionHandler } from '../interfaces/Handler';
import { ButtonRoute } from '../decorators/InteractionConfigurable';
import { PledgeEmbed, PledgeRow } from '../components/bundles/Pledge';
import { Catchable } from '../decorators/Catchable';
import { PledgeStates } from '../../core/library/types/Miscellaneous';
import { PreTrialEmbed, PreTrialRow } from '../components/bundles/PreTrial';
import { SubmitPledge, SubmitPledgeRow } from '../components/bundles/Submit';
import { TrialOneEmbed, TrialOneRow } from '../components/bundles/TrialOne';
import { TrialThreeEmbed, TrialThreeRow } from '../components/bundles/TrialThree';
import { AlreadyCompleteTheRitual } from '../errors/Pledge';
import { TrialTwoEmbed, TrialTwoRow } from '../components/bundles/TrialTwo';

@ButtonRoute('ipa')
export class Ipa extends InteractionHandler<ButtonInteraction> {
declare private userPledgeState: PledgeStates | undefined;

@Catchable()
public async execute(): Promise<void> {
await this.event.deferReply({ flags: MessageFlags.Ephemeral });
await this.setState();

if (this.userPledgeState != undefined && this.userPledgeState >= PledgeStates.Pending) {
await this.showEmbed();
return;
}

await this.event.editReply({
embeds: [new PledgeEmbed().component],
components: [new PledgeRow().component]
});
}

private async showEmbed(): Promise<void> {
const messagePayload = (() => {
switch (this.userPledgeState) {
case PledgeStates.TrialOne:
return {
embeds: [new TrialOneEmbed().component],
components: [new TrialOneRow(this.core.continued.get(this.event.user.id)).component]
};
case PledgeStates.TrialTwo:
return {
embeds: [new TrialTwoEmbed().component],
components: [new TrialTwoRow().component]
};
case PledgeStates.TrialThree:
return {
embeds: [new TrialThreeEmbed().component],
components: [new TrialThreeRow().component]
};
case PledgeStates.Completed:
return {
embeds: [new SubmitPledge().component],
components: [new SubmitPledgeRow().component]
};
case PledgeStates.Submitted:
throw new AlreadyCompleteTheRitual(
`User ID: ${this.event.user.username} has already completed the ritual.`
);
default:
return {
embeds: [new PreTrialEmbed().component],
components: [new PreTrialRow().component]
};
}
})();

await this.event.editReply({ ...messagePayload });
return;
}

private async setState(): Promise<void> {
const ritual = await this.core.db.client.ritual.findFirst({
where: { userId: this.event.user.id },
include: { answers: true }
});
const numAnswers = ritual?.answers.length ?? 0;
let stateToSet: PledgeStates | undefined = undefined;

if (ritual) {
stateToSet = numAnswers === 0 ? PledgeStates.TrialOne : stateToSet; // When user just started
stateToSet = numAnswers >= 7 ? PledgeStates.TrialTwo : stateToSet; // 7 questions done means trial of devotion
stateToSet = numAnswers >= 8 ? PledgeStates.TrialOne : stateToSet; // 8 ques means passed trial 2, and is in trial of devotion
stateToSet = numAnswers >= 10 ? PledgeStates.TrialThree : stateToSet; // 10 questions done means trial of sacrifice
stateToSet = ritual.completed ? PledgeStates.Completed : stateToSet; // Completed ritual
stateToSet = ritual.submitted ? PledgeStates.Submitted : stateToSet; // Submitted ritual
}
if (stateToSet) {
this.core.pledges.set(this.event.user.id, stateToSet);
}

this.userPledgeState = this.core.pledges.get(this.event.user.id);
}
}
import { ButtonInteraction, MessageFlags } from 'discord.js';
import { InteractionHandler } from '../interfaces/Handler';
import { ButtonRoute } from '../decorators/InteractionConfigurable';
import { PledgeEmbed, PledgeRow } from '../components/bundles/Pledge';
import { Catchable } from '../decorators/Catchable';
import { PledgeStates } from '../../core/library/types/Miscellaneous';
import { PreTrialEmbed, PreTrialRow } from '../components/bundles/PreTrial';
import { SubmitPledge, SubmitPledgeRow } from '../components/bundles/Submit';
import { TrialOneEmbed, TrialOneRow } from '../components/bundles/TrialOne';
import { TrialThreeEmbed, TrialThreeRow } from '../components/bundles/TrialThree';
import { AlreadyCompleteTheRitual } from '../errors/Pledge';
import { TrialTwoEmbed, TrialTwoRow } from '../components/bundles/TrialTwo';

@ButtonRoute('ipa')
export class Ipa extends InteractionHandler<ButtonInteraction> {
declare private userPledgeState: PledgeStates | undefined;

@Catchable()
public async execute(): Promise<void> {
await this.event.deferReply({ flags: MessageFlags.Ephemeral });
await this.setState();

if (this.userPledgeState != undefined && this.userPledgeState >= PledgeStates.Pending) {
await this.showEmbed();
return;
}

await this.event.editReply({
embeds: [new PledgeEmbed().component],
components: [new PledgeRow().component]
});
}

private async showEmbed(): Promise<void> {
const messagePayload = (() => {
switch (this.userPledgeState) {
case PledgeStates.TrialOne:
return {
embeds: [new TrialOneEmbed().component],
components: [new TrialOneRow(this.core.continued.get(this.event.user.id)).component]
};
case PledgeStates.TrialTwo:
return {
embeds: [new TrialTwoEmbed().component],
components: [new TrialTwoRow().component]
};
case PledgeStates.TrialThree:
return {
embeds: [new TrialThreeEmbed().component],
components: [new TrialThreeRow().component]
};
case PledgeStates.Completed:
return {
embeds: [new SubmitPledge().component],
components: [new SubmitPledgeRow().component]
};
case PledgeStates.Submitted:
throw new AlreadyCompleteTheRitual(
`User ID: ${this.event.user.username} has already completed the ritual.`
);
default:
return {
embeds: [new PreTrialEmbed().component],
components: [new PreTrialRow().component]
};
}
})();

await this.event.editReply({ ...messagePayload });
return;
}

private async setState(): Promise<void> {
const ritual = await this.core.db.client.ritual.findFirst({
where: { userId: this.event.user.id },
include: { answers: true }
});
const numAnswers = ritual?.answers.length ?? 0;
let stateToSet: PledgeStates | undefined = undefined;

if (ritual) {
stateToSet = numAnswers === 0 ? PledgeStates.TrialOne : stateToSet; // When user just started
stateToSet = numAnswers >= 7 ? PledgeStates.TrialTwo : stateToSet; // 7 questions done means trial of devotion
stateToSet = numAnswers >= 8 ? PledgeStates.TrialOne : stateToSet; // 8 ques means passed trial 2, and is in trial of devotion
stateToSet = numAnswers >= 10 ? PledgeStates.TrialThree : stateToSet; // 10 questions done means trial of sacrifice
stateToSet = ritual.completed ? PledgeStates.Completed : stateToSet; // Completed ritual
stateToSet = ritual.submitted ? PledgeStates.Submitted : stateToSet; // Submitted ritual
}
if (stateToSet) {
this.core.pledges.set(this.event.user.id, stateToSet);
}

this.userPledgeState = this.core.pledges.get(this.event.user.id);
}
}
Amgelo
Amgelo2w ago
hmm I was hoping to completely rule out the third point in the tag if you could know what happens in discord, but given your third point, it's probably ruled out already
materwelon
materwelonOP2w ago
yep... lemmi check though. gimmi a min
Amgelo
Amgelo2w ago
if defers are the first thing you're doing in your handler, sounds like it's either taking way too long to reach your handler, or the request is taking too long to reach discord (network problem)
materwelon
materwelonOP2w ago
could it ever depend on the user's internet connection?
Amgelo
Amgelo2w ago
no, only your bot's
materwelon
materwelonOP2w ago
hmmmm. yeah that shouldnt be an issue then.
Amgelo
Amgelo2w ago
and discord ig but it's not likely to be that since that'd be a known issue at that time
materwelon
materwelonOP2w ago
yeah. in my error logs, I saw that it kept happening to the same user there's also some other weird issue that for some reason, a place where the code is giving users a role, only works sometimes. I still have to test this though. but more on that later
materwelon
materwelonOP2w ago
example
No description
materwelon
materwelonOP2w ago
Could make sense for it to happen in Ask.execute. Because this is all there is
import { ButtonInteraction } from 'discord.js';
import { AnswerModal } from '../components/modals/Answer';
import { Catchable } from '../decorators/Catchable';
import { ButtonRoute } from '../decorators/InteractionConfigurable';
import { InteractionHandler } from '../interfaces/Handler';

@ButtonRoute('answer')
export class Ask extends InteractionHandler<ButtonInteraction> {
@Catchable()
async execute(): Promise<void> {
await this.event.showModal(new AnswerModal().component);
}
}
import { ButtonInteraction } from 'discord.js';
import { AnswerModal } from '../components/modals/Answer';
import { Catchable } from '../decorators/Catchable';
import { ButtonRoute } from '../decorators/InteractionConfigurable';
import { InteractionHandler } from '../interfaces/Handler';

@ButtonRoute('answer')
export class Ask extends InteractionHandler<ButtonInteraction> {
@Catchable()
async execute(): Promise<void> {
await this.event.showModal(new AnswerModal().component);
}
}
Amgelo
Amgelo2w ago
we'd have to take a look at what happens from the event emit to that execute call, in order to be able to see whether it makes sense but that'd be tedious for everyone
materwelon
materwelonOP2w ago
Also, no. no user has reported anything yet. Which is unfortunate. I wish it were easy to reproduce the issue we do know that the error always happens inside the handler. Based on the stack trace at least. so it does get to the handler
Amgelo
Amgelo2w ago
I'd recommend considering these options, maybe you could debug by logging the time before replying and Interaction#createdAt, or well, the difference
materwelon
materwelonOP2w ago
but don't know how long it takes to get to the handler. there is no awaited processing before it gets to the handler. That should not be a problem though hmmm. Okay. Will try this out.
Amgelo
Amgelo2w ago
could also include the timestamp in those message logs
materwelon
materwelonOP2w ago
that's actually a great idea. also will prevent flooding logs because i'll only get them when an error occurs
Amgelo
Amgelo2w ago
if possible you can also include the interaction timestamp, considering the url is in the error, which contains the interaction id, which is a snowflake, which is a timestamp
d.js docs
d.js docs2w ago
:discord: API Reference - Convert Snowflake to DateTime read more
materwelon
materwelonOP2w ago
where dyu see the URL in the error btw? I was trying to figure out a way to do this because I'm not passing the interaction object in my webhook error logger atm
import { WebhookClient } from 'discord.js';
import { RegisterHook } from '../../decorators/RegisterHook';
import { HookedEvents, Hooks } from '../../interfaces/Hooks';
import { WebhookLog } from '../../interfaces/abstracts/WebhookLog';
import { BuilderComponent } from '../../../../bot/interfaces/Components';
import { Images } from '../../../library/globals/Assets';

@RegisterHook(Hooks.UnknownException)
export class UnknownException extends WebhookLog<Hooks.UnknownException> {
webhook = new WebhookClient({
url: ''
});

async execute() {
await this.webhook.send({
username: 'Unknown Exception',
avatarURL: Images.Error,
embeds: [new UnhandledErrorEmbed(this.data).component]
});
}
}

class UnhandledErrorEmbed extends BuilderComponent<'embed'> {
constructor(data: HookedEvents[Hooks.UnknownException]) {
super('embed');

const [uuid, error, guild, user] = data;

this.instance
.setTitle(`An unknown exception was thrown`)
.setColor('#ef4860')
.setDescription(
`**Guild ID:** \`${guild.id}\`\n` +
`**Guild Name:** ${guild.name}\n` +
`**User ID:** \`${user.id}\`\n` +
`**Username:** ${user.username}\n` +
`### UUID: \`${uuid}\`\n` +
'\`\`\`' +
error.stack +
'\`\`\`'
);
}
}
import { WebhookClient } from 'discord.js';
import { RegisterHook } from '../../decorators/RegisterHook';
import { HookedEvents, Hooks } from '../../interfaces/Hooks';
import { WebhookLog } from '../../interfaces/abstracts/WebhookLog';
import { BuilderComponent } from '../../../../bot/interfaces/Components';
import { Images } from '../../../library/globals/Assets';

@RegisterHook(Hooks.UnknownException)
export class UnknownException extends WebhookLog<Hooks.UnknownException> {
webhook = new WebhookClient({
url: ''
});

async execute() {
await this.webhook.send({
username: 'Unknown Exception',
avatarURL: Images.Error,
embeds: [new UnhandledErrorEmbed(this.data).component]
});
}
}

class UnhandledErrorEmbed extends BuilderComponent<'embed'> {
constructor(data: HookedEvents[Hooks.UnknownException]) {
super('embed');

const [uuid, error, guild, user] = data;

this.instance
.setTitle(`An unknown exception was thrown`)
.setColor('#ef4860')
.setDescription(
`**Guild ID:** \`${guild.id}\`\n` +
`**Guild Name:** ${guild.name}\n` +
`**User ID:** \`${user.id}\`\n` +
`**Username:** ${user.username}\n` +
`### UUID: \`${uuid}\`\n` +
'\`\`\`' +
error.stack +
'\`\`\`'
);
}
}
materwelon
materwelonOP2w ago
great! okay lemmi do that dyu have an example url for an interaction pls? If not, i'll throw an error and get it. Would be faster if you already have an example though :Pray:
materwelon
materwelonOP2w ago
noice thanks
materwelon
materwelonOP2w ago
Alright. I'll push this to prod and observe for a while :thumbsup:
No description
materwelon
materwelonOP4d ago
Note that that 5s 253ms tells me that the processing of the interaction took 253ms. The 5s is the timeout i set for testing. I don't know how or why but i haven't seen a single Unknown Interaction error since I added timestamps in the embed. :roouhh:
materwelon
materwelonOP2d ago
@ButtonRoute('pledge')
@ModalRoute('pledge')
export class Pledge extends InteractionHandler<ButtonInteraction | ModalSubmitInteraction> {
@Catchable()
public async execute() {
if (this.event.isButton()) {
await this.event.showModal(new IpaModal().component);
return;
}

await this.event.deferUpdate();

const pledge = this.event.fields.getTextInputValue('pledge').toLowerCase();

Terms.instance.use(pledge);

// Set the pledge state to pending because user used a valid term
this.core.pledges.set(this.event.user.id, PledgeStates.Pending);

const guildMember = await UserUtils.fetchGuildMember(this.event.guild!, this.event.user.id);
await guildMember.roles.add(Globals.pledgeZeroRole);

await this.event.editReply({
embeds: [new PreTrialEmbed().component],
components: [new PreTrialRow().component]
});
}
}
@ButtonRoute('pledge')
@ModalRoute('pledge')
export class Pledge extends InteractionHandler<ButtonInteraction | ModalSubmitInteraction> {
@Catchable()
public async execute() {
if (this.event.isButton()) {
await this.event.showModal(new IpaModal().component);
return;
}

await this.event.deferUpdate();

const pledge = this.event.fields.getTextInputValue('pledge').toLowerCase();

Terms.instance.use(pledge);

// Set the pledge state to pending because user used a valid term
this.core.pledges.set(this.event.user.id, PledgeStates.Pending);

const guildMember = await UserUtils.fetchGuildMember(this.event.guild!, this.event.user.id);
await guildMember.roles.add(Globals.pledgeZeroRole);

await this.event.editReply({
embeds: [new PreTrialEmbed().component],
components: [new PreTrialRow().component]
});
}
}
@Amgelo hmmmmmm. what to do
No description
materwelon
materwelonOPthis hour
No description
Amgelo
Amgelothis hour
yeah both say more than 3s 6s is way too much as well ^^
materwelon
materwelonOPthis hour
yeah but how do I fix this it happens randomly why are both 524ms
Amgelo
Amgelo23h ago
are you using any kind of proxy?
materwelon
materwelonOP7h ago
None that I know of. But again, I'm not too familiar with proxies, so if I am, I wouldn't know that I am. I'm using Railway to host my bot. Does that help?
treble/luna
treble/luna5h ago
isnt railway one of those free hosts that use shared ip's? If so theres your issue

Did you find this page helpful?