Skip to main content

Tracing & Debugging

FlowCord includes a NavigationTracer that records every menu transition during a session. It is disabled by default and intended for development and debugging.

Enable it in the FlowCord config:

const flowcord = new FlowCord({
client,
enableTracing: true,
});

Once enabled, every navigation event is recorded. Access the events via flowcord.engine.tracer:

const events = flowcord.engine.tracer.events;
console.log(events);
// [
// { from: 'main', to: 'settings', sessionId: '...', userId: '...', timestamp: 1234567890, trigger: 'button:open-settings' },
// { from: 'settings', to: 'permissions', sessionId: '...', userId: '...', timestamp: 1234567891 },
// ]
FieldTypeDescription
fromstringMenu navigated away from
tostringMenu navigated to
sessionIdstringSession identifier
userIdstringDiscord user ID
timestampnumberUnix timestamp (ms)
triggerstring | undefinedComponent that triggered navigation (e.g. 'button:confirm')

Querying paths​

tracer.getPathsFrom(menuId) returns all navigation paths originating from a given menu, as arrays of menu names:

const paths = flowcord.engine.tracer.getPathsFrom('main');
// [['main', 'settings', 'permissions'], ['main', 'shop', 'cart']]

This is useful for verifying that your navigation graph matches your intent.

Clearing events​

flowcord.engine.tracer.clear();
note

Navigation tracing is in-memory and process-scoped. Events accumulate for the lifetime of the process. Clear them periodically if running the bot for long sessions or testing many flows.


Error handling​

Default behaviour​

If no onError handler is configured, FlowCord catches unhandled session errors and sends the user an ephemeral dark-red embed with the error message. If the interaction has already been deferred or replied to, it edits the existing reply instead.

Custom error handler​

Pass onError to the FlowCord config to override the default:

const flowcord = new FlowCord({
client,
onError: async (session, error) => {
console.error(`[session:${session.id}] Error:`, error);

// Send a custom error message
const interaction = session.commandInteraction;
const embed = new EmbedBuilder()
.setColor('Red')
.setDescription('Something went wrong. Please try again.');

if (interaction.deferred || interaction.replied) {
await interaction.editReply({ embeds: [embed], components: [] });
} else {
await interaction.reply({ embeds: [embed], components: [], ephemeral: true });
}
},
});

session is the MenuSession instance — you can access session.id and other session properties for logging context.


Common errors​

Menu "x": must call either setEmbeds() or setLayout() The builder's .build() was called without setting a render callback. Every menu must have either .setEmbeds() or .setLayout().

Menu "x": cannot use both setEmbeds() and setLayout() Both render modes were called on the same builder. Pick one — they are mutually exclusive per menu.

Menu "x": select menus cannot be used with button pagination .setSelectMenu() and .setButtons(..., { pagination }) were both set. Remove one or restructure the flow.

Button "x" is configured as a modal trigger but no matching modal was found A button has opensModal: 'some-id' but .setModal() does not return a modal config with id: 'some-id'. Check that the IDs match exactly.

Button "x" used openModal() action after the interaction was deferred The openModal() action was used in a button that was already deferred. Use opensModal on the button config instead — FlowCord uses it to call showModal() before the interaction is deferred.


Session internals​

For a deeper look at how sessions work — the render-await-dispatch loop, in-memory session lifecycle, and what that means for bot restarts and scalability — see ARCHITECTURE.md in the flowcord-core repository.