Free rules / discord.js
discord.js v14 Rules for AI Coding (free)
Ask an AI coding tool for a Discord bot and you'll often get code from older tutorials: ephemeral: true, fetchReply, the old ready event, slow handlers that hit “The application did not respond”. This free rules file tells your agent which discord.js v14 options are current, what Discord's limits are, and how to keep the token safe.
Old code agents still write, and what to use instead
| Instead of | Use | Why |
|---|---|---|
reply({ ephemeral: true }) | reply({ flags: MessageFlags.Ephemeral }) | ephemeral is marked @deprecated in favour of flags |
reply({ fetchReply: true }) | reply({ withResponse: true }) | returns an InteractionCallbackResponse; the message is response.resource?.message |
client.once("ready", …) | client.once(Events.ClientReady, …) | the "ready" event name is deprecated; the new name is "clientReady" |
.setDMPermission(false) | .setContexts(InteractionContextType.Guild) | setDMPermission is deprecated in @discordjs/builders |
new MessageEmbed() / Intents.FLAGS | new EmbedBuilder() / GatewayIntentBits | v13 names that no longer exist in v14 |
The deprecated forms still compile in 14.27, which is why agents don't notice. Checked in discord.js 14.27.0's type definitions on .
How to use it
- Cursor: save the file below as
.cursor/rules/discordjs-v14.mdc. It applies to your.ts/.jsfiles. - Claude Code, Codex and others: paste everything below the
---header intoCLAUDE.mdorAGENTS.mdin your repo root. - Ask the agent to review an existing command against the rules, e.g. “Check src/commands for deprecated discord.js options and handlers that could take over 3 seconds.”
The rules file
---
description: discord.js v14 rules (current options, deprecated ones to avoid, Discord's limits)
globs: **/*.ts, **/*.js
alwaysApply: false
---
# discord.js v14 rules
Use discord.js v14 (checked against 14.27). When unsure whether a method or option exists, check `node_modules/discord.js/typings/index.d.ts` instead of guessing. Most examples online are v13 or early v14.
## Use the current options, not the deprecated ones
- Ready event: `client.once(Events.ClientReady, (c) => ...)`. The `"ready"` event name is deprecated in favour of `"clientReady"`.
- Ephemeral replies: `interaction.reply({ content, flags: MessageFlags.Ephemeral })`. Don't write `ephemeral: true` (deprecated).
- Getting the reply back: `reply({ ..., withResponse: true })` returns an InteractionCallbackResponse; the message is `response.resource?.message`. Don't write `fetchReply: true` (deprecated).
- Where a command can be used: `.setContexts(InteractionContextType.Guild)` (or BotDM, PrivateChannel). Don't write `.setDMPermission()` (deprecated).
- Who can use a command by default: `.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)`. It applies to the whole command; for per-subcommand rules check `interaction.memberPermissions?.has(...)` in code.
- No v12/v13 APIs: `GatewayIntentBits` (not `Intents.FLAGS`), `EmbedBuilder` (not `MessageEmbed`), `ActionRowBuilder`/`ButtonBuilder` (not `MessageActionRow`/`MessageButton`), `Events.InteractionCreate` (not `"interaction"`).
- Narrow the interaction union with type guards (`isChatInputCommand()`, `isAutocomplete()`, `isButton()`, `inGuild()`), not casts.
- Required options: `interaction.options.getString("name", true)`. Without `true` it returns null when the option is missing.
## Discord's rules (from the Discord developer docs)
- Send the first response to every interaction within 3 seconds (reply, deferReply, update, deferUpdate, showModal, or respond for autocomplete). For slow work, `await interaction.deferReply()` first, then `editReply()`.
- The interaction token lasts 15 minutes. editReply and followUp fail after that.
- Respond once. After reply or deferReply, use editReply or followUp. In a catch block, check `interaction.replied || interaction.deferred` and use followUp, or you get InteractionAlreadyReplied.
- Autocomplete can't be deferred and returns at most 25 choices. Answer from memory or a fast local query.
- Message content is at most 2000 characters. Component custom_id is 1-100 characters.
- Button and select custom_ids come back from the user's client: parse and validate them like any user input.
- Anything a user typed (tags, names, reasons) goes out with `allowedMentions: { parse: [] }` so it can't ping @everyone, roles or people.
## Intents
- Slash-command bots need only `GatewayIntentBits.Guilds`. Add an intent only when a feature needs it.
- MessageContent, GuildMembers and GuildPresences are privileged. Enable them in the Developer Portal (Bot page) first, or the gateway closes the connection (close code 4014). Prefer a slash command over reading message content.
## Commands and registration
- Register commands with a separate script, not on every startup: `rest.put(Routes.applicationGuildCommands(clientId, guildId), { body })` for one test server (updates instantly), `Routes.applicationCommands(clientId)` for global.
- PUT is a bulk overwrite: commands missing from the body are deleted.
- Discord allows 200 command creates per day per server, so never register in a loop.
- Command names are lowercase, 1-32 characters; descriptions 1-100. `SlashCommandBuilder#toJSON()` validates them, so call it in a test.
## Safety
- The bot token comes from an env var (e.g. DISCORD_TOKEN) loaded from .env or the host's secret settings. Never print, log, commit or hard-code it. If it leaks, reset it in the Developer Portal.
- Wrap command handlers so one error logs the stack and sends the user an ephemeral message instead of "The application did not respond".
- Handle SIGINT/SIGTERM: `await client.destroy()`, close the database, then exit.
- A gateway bot is a long-running process. It can't run on serverless functions; host it on a VPS or a worker/background service.
Common questions
Why does my AI keep writing ephemeral: true?
Most discord.js examples online predate the change. In discord.js 14.27 the ephemeral option still type-checks but is marked deprecated; the current form is flags: MessageFlags.Ephemeral. A rules file tells the agent which form to use.
What does "The application did not respond" mean?
Discord expects the first response to an interaction within 3 seconds. If your handler does slow work, call interaction.deferReply() first and then editReply() when it's done. The interaction token stays valid for 15 minutes.
Which intents does a slash-command bot need?
Only GatewayIntentBits.Guilds. MessageContent, GuildMembers and GuildPresences are privileged: enable them in the Developer Portal first, or Discord closes the gateway connection.
Sources: discord.js 14.27.0 type definitions; Discord developer docs on receiving and responding to interactions, application commands and privileged intents.
Want a bot that already follows these rules? The Discord Bot Cartridge ($29) is a working discord.js + TypeScript bot with slash commands, SQLite, tests that run without a token, and these rules built in.