Skip to content

fix(events): prevent double execution of slash commands and components#121

Draft
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-correctness-bugs-9e07
Draft

fix(events): prevent double execution of slash commands and components#121
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-correctness-bugs-9e07

Conversation

@cursor

@cursor cursor Bot commented Jun 12, 2026

Copy link
Copy Markdown

Bug and impact

Slash commands and UI components (buttons, select menus, modals) could execute twice for a single Discord interaction. This caused duplicated economy transfers (e.g. /rob, /deposit), duplicate replies, and other side effects.

Root cause

src/handlers/events.js registered three separate client.on("interactionCreate") listeners:

  1. Validators folder (runs chatInputCommandValidator, buttonValidator, etc. — each calls .run())
  2. Guild/interactionCreate.js (also calls .run() for slash commands)
  3. Guild/components.js (also calls .run() for buttons/selects/modals)

Node.js invokes async event listeners concurrently. The interaction.replied || interaction.deferred guards in the Guild handlers assumed validators had already finished, but both listeners started before either replied.

This regressed when per-module event registration was restored in #67/#72.

Fix

Defer Guild modules with event: "interactionCreate" into an interactionCreateFollowups chain that runs sequentially after validators inside the single validations listener. Guild handlers still skip via the existing replied/deferred check once validators handle the interaction.

Validation

  • Added tests/interaction-create-single-listener.test.js documenting the race and asserting the chained registration pattern
  • Updated tests/events-handler-shape.test.js
  • npm test — 40/40 passing
Open in Web View Automation 

…tion

Validators and Guild interactionCreate/components modules each registered
separate client.on('interactionCreate') listeners. Node emits those listeners
concurrently, so slash commands and UI components could run twice before
interaction.replied was set — corrupting economy balances and duplicating side
effects.

Defer Guild interactionCreate modules into a followup chain that runs
sequentially after validators inside the single validations listener.

Co-authored-by: zVapor_ <contact@zvapor.xyz>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant