Interaction replying with string works but doesn't work when I return a button

Hey, I started creating a discord bot. It returns a link for the user to click on but it's pretty ugly so i wanted to move over to a button, but when I do this, I am not getting any errors in my console, but running the slash command gives me "An error has occurred." in discord. Currently this works fine: await interaction.reply({ content: 'Please log in on our website to sync your account: ' + url}); I copied the official discord.js example with plain strings to keep things simple but even that won't work. If I replace the above line with this I'll get the error:
const confirm = new ButtonBuilder()
.setCustomId('confirm')
.setLabel('Confirm')
.setStyle(ButtonStyle.Danger);

const cancel = new ButtonBuilder()
.setCustomId('cancel')
.setLabel('Cancel')
.setStyle(ButtonStyle.Secondary);

const row = new ActionRowBuilder()
.addComponents(cancel, confirm);

await interaction.reply({
content: `Are you sure?`,
components: [row],
});
const confirm = new ButtonBuilder()
.setCustomId('confirm')
.setLabel('Confirm')
.setStyle(ButtonStyle.Danger);

const cancel = new ButtonBuilder()
.setCustomId('cancel')
.setLabel('Cancel')
.setStyle(ButtonStyle.Secondary);

const row = new ActionRowBuilder()
.addComponents(cancel, confirm);

await interaction.reply({
content: `Are you sure?`,
components: [row],
});
11 Replies
d.js toolkit
d.js toolkit11mo ago
- What's your exact discord.js npm list discord.js and node node -v version? - Post the full error stack trace, not just the top part! - Show your code! - Explain what exactly your issue is. - Not a discord.js issue? Check out #useful-servers. - Issue solved? Press the button!
treble/luna
treble/luna11mo ago
How do you handle the interactionCreate event?
GoatSniff
GoatSniff11mo ago
App.js
client.slashCommands = new Collection();
const slashCommands = [];

const commandSlashFiles = fs.readdirSync('./commands').filter(file => file.endsWith(".js"));

// Load all slash commands from ./commands
for (const fileSlash of commandSlashFiles) {
const commandSlash = require(`./commands/${fileSlash}`);
client.slashCommands.set(commandSlash.data.name, commandSlash);
slashCommands.push(commandSlash.data.toJSON());
console.log(`${commandSlash.data.name}.js has loaded.`);
}
client.slashCommands = new Collection();
const slashCommands = [];

const commandSlashFiles = fs.readdirSync('./commands').filter(file => file.endsWith(".js"));

// Load all slash commands from ./commands
for (const fileSlash of commandSlashFiles) {
const commandSlash = require(`./commands/${fileSlash}`);
client.slashCommands.set(commandSlash.data.name, commandSlash);
slashCommands.push(commandSlash.data.toJSON());
console.log(`${commandSlash.data.name}.js has loaded.`);
}
Then in my sync.js (the name of my slashcommand)
module.exports = {
data: new SlashCommandBuilder()
.setName('sync')
.setDescription('Sync your account!'),
async execute(interaction) {
// etc....
module.exports = {
data: new SlashCommandBuilder()
.setName('sync')
.setDescription('Sync your account!'),
async execute(interaction) {
// etc....
I'd have thought loading shouldn't be the issue since replying with a string is working without a hitch?
treble/luna
treble/luna11mo ago
thats not ypur interactionCreate eve't
GoatSniff
GoatSniff11mo ago
My mistake, one second
const { Events } = require('discord.js');
const config = require('../configs/config.json');

const timers = [];

function beginRateLimitTimer(userID) {
timers.push({ userID, timer: setTimeout(endRateLimitTimer, config.rateLimitMS, userID) })
}

// Called when rate limit timer is reached
function endRateLimitTimer(userID) {
const index = timers.findIndex(t => t.userID === userID);
if (index !== -1) {
timers.splice(index, 1);
}
}

function isRateLimited(userID) {
return timers.some(t => t.userID == userID);
}

module.exports = {
name: Events.InteractionCreate,
once: false,
async execute(interaction) {
if (isRateLimited(interaction.user.id)) {
return await interaction.reply({ content: `Please wait before using another slash command.`, ephemeral: true });
}

if (interaction.isCommand()) {
const slashCommand = interaction.client.slashCommands.get(interaction.commandName);
if (!slashCommand) return;

beginRateLimitTimer(interaction.user.id);

try {
await slashCommand.execute(interaction);
} catch (err) {
await interaction.reply({ content: `An error has occured.`, ephemeral: true });
}
}
},
};
const { Events } = require('discord.js');
const config = require('../configs/config.json');

const timers = [];

function beginRateLimitTimer(userID) {
timers.push({ userID, timer: setTimeout(endRateLimitTimer, config.rateLimitMS, userID) })
}

// Called when rate limit timer is reached
function endRateLimitTimer(userID) {
const index = timers.findIndex(t => t.userID === userID);
if (index !== -1) {
timers.splice(index, 1);
}
}

function isRateLimited(userID) {
return timers.some(t => t.userID == userID);
}

module.exports = {
name: Events.InteractionCreate,
once: false,
async execute(interaction) {
if (isRateLimited(interaction.user.id)) {
return await interaction.reply({ content: `Please wait before using another slash command.`, ephemeral: true });
}

if (interaction.isCommand()) {
const slashCommand = interaction.client.slashCommands.get(interaction.commandName);
if (!slashCommand) return;

beginRateLimitTimer(interaction.user.id);

try {
await slashCommand.execute(interaction);
} catch (err) {
await interaction.reply({ content: `An error has occured.`, ephemeral: true });
}
}
},
};
I guess that explains my error message 😅 Forgot I even added that I'll log that error out, one second
treble/luna
treble/luna11mo ago
I dont see any button handler there
GoatSniff
GoatSniff11mo ago
That'd be it then :D, I didn't know there was special handling needed but I will have a look through the docs
treble/luna
treble/luna11mo ago
you can use the .isButton typeguard and then do your logic there
GoatSniff
GoatSniff11mo ago
Is that typescript specific?
Squid
Squid11mo ago
No It does narrow your typings, but it also has the runtime effect of only returning true if the interaction is a button interaction
GoatSniff
GoatSniff11mo ago
Okay I figured this out. The example I gave you from the docs wasn't "true to life" as all I wanted to do was have a button with a URL which doesn't require any special interaction handling logic since it's just a button with a URL The core issue was just that I didn't import the ButtonBuilder, ButtonStyle, etc and I was catching all the errors which I didn't realise so I never saw the true cause. All fixed now, thanks all for your help 👍