Slash Commands Not Showing Up? A Troubleshooting Guide
A systematic checklist for Discord slash commands that don't appear, show up twice, or fail with "The application did not respond" — and how to fix each cause.
On this page
- First, understand how commands get to users
- Step 1: Were the commands registered at all?
- Step 2: Global or guild?
- Step 3: Did one script overwrite another?
- Step 4: Is the definition valid?
- Step 5: Was the bot invited correctly?
- Step 6: Is the server hiding it?
- When commands appear but fail
- Don’t register on every start
- A fast diagnostic checklist
- Summary
You wrote the command, deployed the bot, typed / — and nothing. Slash commands have more moving parts than message commands, and when they don’t appear, the cause can be in your code, your registration script, your invite link or the server’s settings. This guide works through them in the order that finds the problem fastest.
First, understand how commands get to users
A slash command reaches a user in three separate steps:
- Registration. Your bot sends the command definition to Discord’s API, either globally or for one server (a guild command). This is a REST call, completely separate from your bot logging in.
- Authorisation. The bot must be in the server with permission to create commands, and the server’s settings must allow the user to see it.
- Handling. When someone runs the command, Discord sends an interaction to your bot, which must acknowledge it within three seconds.
“Not showing up” is a problem with step 1 or 2. “Showing up but failing” is step 3.
Step 1: Were the commands registered at all?
The most common cause by far is that registration never ran, or ran against the wrong application.
- Did you run your registration script? Deploying code doesn’t register commands. In discord.js that’s a script calling
rest.put(Routes.applicationCommands(...)); in discord.py it’sbot.tree.sync(); in JDA it’supdateCommands(). - Did it succeed? Log the response. discord.py’s
sync()returns the list of synced commands — print its length. A count of zero means your commands weren’t added to the tree, usually because the cog wasn’t loaded before syncing. - Is the application ID correct? If you have separate test and production bots, it’s easy to register commands for one while running the other. The ID in the route must match the bot’s application.
A quick way to see what Discord actually has is to fetch the registered list:
const cmds = await rest.get(Routes.applicationCommands(process.env.CLIENT_ID));
console.log(cmds.map((c) => c.name));
Step 2: Global or guild?
Commands can be registered in two scopes, and mixing them causes confusion.
| Scope | Where it appears | Update speed | Use for |
|---|---|---|---|
| Guild | One server only | Immediate | Development and testing |
| Global | Every server the bot is in, and DMs if allowed | Can take a little while to reach clients | Production |
If a global command doesn’t appear yet, fully refresh your Discord client (Ctrl+R on desktop) — clients cache the command list. During development, register to a test guild so changes appear instantly.
Commands showing up twice? You’ve registered the same command both globally and to a guild. Clear the guild set by registering an empty list: rest.put(Routes.applicationGuildCommands(appId, guildId), { body: [] }).
Step 3: Did one script overwrite another?
Registration with PUT (discord.js), sync() (discord.py) and updateCommands() (JDA) replaces the entire command list for that scope. If you have two scripts — say, one that registers admin commands and another that registers user commands — whichever runs last wins, and the other set disappears. Always register the complete list in one call.
Step 4: Is the definition valid?
Discord rejects invalid definitions, sometimes with an error your script swallows. Check against the rules:
- Names are 1–32 characters, lowercase, with no spaces (letters, numbers,
-and_). - Descriptions are 1–100 characters and required for slash commands.
- A command can have up to 25 options, and required options must come before optional ones.
- An application can have up to 100 global slash commands.
- Choices lists are limited to 25 entries — use autocomplete for more.
Wrap your registration call in a try/catch and print the full error body; Discord’s error messages point to the exact field.
Step 5: Was the bot invited correctly?
Older invite links sometimes lacked the applications.commands scope. To be safe, generate a fresh invite in the Developer Portal with both the bot and applications.commands scopes selected, and re-invite the bot. Re-inviting doesn’t remove it or reset its roles.
Step 6: Is the server hiding it?
Server administrators can restrict commands under Server Settings → Integrations. A command can be disabled for @everyone, limited to certain roles or channels, or hidden entirely. Your code also controls visibility:
setDefaultMemberPermissions()(discord.js) ordefault_permissions(discord.py) hides a command from members who lack those permissions. If you setAdministrator, only admins will see it.- Commands can be restricted from DMs. If you’re testing in a DM, check the command allows that context.
If the command appears for you but not for a user, permissions are almost certainly the reason.
When commands appear but fail
“The application did not respond”
Discord waited three seconds and your bot never acknowledged the interaction. Causes:
- The bot isn’t running or isn’t receiving events. Check the console. If the process is up, confirm it’s logged in as the same application you registered commands for.
- No handler matches. The command name in your handler doesn’t match the registered name, or the handler file wasn’t loaded.
- The handler is too slow. Database queries and API calls can exceed three seconds. Acknowledge first, then do the work:
await interaction.deferReply();
const result = await slowOperation();
await interaction.editReply(result);
In discord.py, use await interaction.response.defer() then await interaction.followup.send(...).
- The handler threw an error before replying. Look for a stack trace in the console. Catch errors per command and reply with an error message so users aren’t left hanging — see auto-restart and crash recovery.
“Unknown interaction”
You tried to respond after the three-second window closed, or responded twice with reply(). Defer earlier, and use followUp() or editReply() for any response after the first.
“Interaction has already been acknowledged”
Two parts of your code both replied — often two copies of the bot running at once, such as one on your PC and one on the server. Stop the duplicate.
Don’t register on every start
It’s tempting to register commands inside the ready event so they’re always current. On a host with automatic restarts and push-to-deploy, that sends the full command list to Discord many times a day, which wastes API calls and can hit rate limits. Register only when definitions change, from a separate script or an owner-only command. Our discord.js and discord.py deployment guides show both approaches.
A fast diagnostic checklist
- Fetch the registered commands from the API — are yours there?
- Right scope? Guild for testing, global for production, never both for the same command.
- One registration call with the full list?
- Valid names, descriptions and option order?
- Invite includes
applications.commands? - Server integration settings and default permissions allow the user?
- Client refreshed with Ctrl+R?
- Bot running, logged in as the same application, handler matching the name?
Summary
Missing slash commands are usually a registration problem: the script didn’t run, targeted the wrong application, used the wrong scope, or was overwritten by another registration. Visibility problems come from invite scopes and server permission settings. Failures after the command appears come from slow or missing handlers — defer early, catch errors, and make sure only one copy of your bot is running.