LogoRobo.js

Sage Mode

Automatic replies, deferrals, and error handling

Sage automatically handles how your bot responds to commands — sending replies, deferring slow commands, and formatting error messages during development.

For the Sage resolution chain, async-only deferral behavior, and context menu support, see the @robojs/discordjs Sage reference.

Automatic Replies

Return a value from your command handler instead of calling interaction.reply(). Sage detects the return value and sends it as the reply.

A string return sends a plain text reply.

src/commands/ping.ts
export default () => {
	return 'Pong!'
}
src/commands/ping.js
export default () => {
	return 'Pong!'
}

An object return supports full InteractionReplyOptions, including ephemeral messages.

src/commands/ping.ts
export default () => {
	return { content: 'Pong!', ephemeral: true }
}
src/commands/ping.js
export default () => {
	return { content: 'Pong!', ephemeral: true }
}

You can also return embeds and other Discord.js structures.

src/commands/ping.ts
import { EmbedBuilder } from 'discord.js'

export default () => {
	const embed = new EmbedBuilder()
		.setTitle('Pong!')
		.setDescription('Response time: 42ms')

	return { embeds: [embed] }
}
src/commands/ping.js
import { EmbedBuilder } from 'discord.js'

export default () => {
	const embed = new EmbedBuilder()
		.setTitle('Pong!')
		.setDescription('Response time: 42ms')

	return { embeds: [embed] }
}

If you call interaction.reply() yourself, Sage steps aside and does not send a duplicate reply.

Discord showing three different bot responses: a plain text 'Pong!' reply, an ephemeral reply marked 'Only you can see this', and a rich embed response with title and description, all from the same bot

FocusThe three different reply types: plain text, ephemeral, and embedZoom100%NotesShow three sequential command interactions demonstrating Sage mode: (1) /ping returning 'Pong!' as plain text, (2) an ephemeral reply, (3) a command returning an embed. All from the same bot.

Deferred Replies

When a command takes longer than 250ms to respond, Sage automatically defers the interaction. This prevents Discord from showing an error to the user while your command finishes processing.

Without Sage, you must manually defer and edit the reply.

src/commands/slow.ts
import type { ChatInputCommandInteraction } from 'discord.js'

export default async (interaction: ChatInputCommandInteraction) => {
	await interaction.deferReply()
	const data = await fetchSlowData()
	await interaction.editReply(`Result: ${data}`)
}
src/commands/slow.js
export default async (interaction) => {
	await interaction.deferReply()
	const data = await fetchSlowData()
	await interaction.editReply(`Result: ${data}`)
}

With Sage, return the result and deferral is handled for you.

src/commands/slow.ts
export default async () => {
	const data = await fetchSlowData()
	return `Result: ${data}`
}
src/commands/slow.js
export default async () => {
	const data = await fetchSlowData()
	return `Result: ${data}`
}

Sage only defers when needed. Commands that respond within the buffer period skip deferral entirely, avoiding the "thinking..." indicator for fast responses.

Discord showing a bot interaction with the 'Bot is thinking...' deferred reply indicator visible, demonstrating automatic deferral for slow commands

FocusThe 'thinking...' indicator and eventual responseZoom100%NotesShow a Discord interaction where a slow command has been deferred. Ideally capture the 'BotName is thinking...' state or the final response that replaced it.

Error Replies

In development mode, Sage sends detailed error messages when a command handler throws an exception. These messages include the file path, stack trace, and a time-locked log snapshot showing what happened leading up to the error.

Discord showing a Sage error reply in development mode with a red-tinted embed containing the error message, file path, stack trace excerpt, and a log snapshot section

FocusThe detailed error embed with file path, stack trace, and log snapshotZoom100%NotesTrigger an error in a command handler while running in dev mode. Sage displays a detailed error embed with red color, error message, file path, stack trace excerpt, and log snapshot.

Background Errors

Errors that occur outside of command or event handlers are forwarded to a debug channel when DISCORD_DEBUG_CHANNEL_ID is set in your environment variables. This prevents uncaught exceptions from crashing the bot while keeping you informed. See the Debugging page for more on setting up the debug channel.

Production Behavior

In production, Sage hides error details from users to keep channels clean. If you host with RoboPlay (a hosting platform for Robo.js projects), the dashboard provides a debugging section with full error details, stack traces, and log snapshots.

Configuration

Sage options can be set globally for all commands or per-command.

OptionTypeDefaultDescription
deferbooleantrueEnable automatic deferrals
deferBuffernumber250Milliseconds before deferring
ephemeralbooleanfalseMake replies ephemeral
errorChannelIdstring-Channel for error messages
errorMessagestring-Custom error message
errorRepliesbooleantrueShow error replies

Global Configuration

Set Sage options for your entire bot in the Discord plugin config file.

config/plugins/robojs/discordjs.ts
import type { DiscordConfig } from '@robojs/discordjs'

export default {
	sage: {
		defer: true,
		deferBuffer: 500,
		ephemeral: false
	}
} satisfies DiscordConfig
config/plugins/robojs/discordjs.mjs
export default {
	sage: {
		defer: true,
		deferBuffer: 500,
		ephemeral: false
	}
}

Per-Command Configuration

Override global settings for individual commands through the command's config export.

src/commands/slow.ts
import { createCommandConfig } from '@robojs/discordjs'

export const config = createCommandConfig({
	description: 'A slow command',
	sage: {
		defer: true,
		deferBuffer: 1000
	}
})
src/commands/slow.js
import { createCommandConfig } from '@robojs/discordjs'

export const config = createCommandConfig({
	description: 'A slow command',
	sage: {
		defer: true,
		deferBuffer: 1000
	}
})

Next Steps

On this page