Guide

Discord bot is online but not responding

Green dot, no replies. The bot connected to Discord, so the token is fine. Something between the message arriving and your command running is going wrong, and it is almost always one of these.

7 minute read

Work down the list in order. The first three account for most cases, and none of them print an error, which is why this is so confusing the first time.

1. Message Content intent is off

Since September 2022, bots only see the text of messages if they have the Message Content intent. Without it, message.content is an empty string for almost everything, so a prefix command like !ping never matches. The bot is online, receives the message, sees nothing in it, and does nothing.

It has to be on in two places:

  1. The Developer Portal: your application, Bot, then Message Content Intent.
  2. Your code:
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix="!", intents=intents)

Messages that mention the bot, and DMs, still include content without the intent. So if @YourBot ping works and !ping does not, this is your problem.

Slash commands do not need this intent at all, which is one reason Discord pushes them.

2. Your on_message is eating the commands

This one catches almost everyone once. If you add an on_message event to a commands.Bot, it replaces the built-in one that runs your commands:

@bot.event
async def on_message(message):
    if "hello" in message.content:
        await message.channel.send("hi")
    # every !command is now silently ignored

Either hand the message back at the end:

@bot.event
async def on_message(message):
    if "hello" in message.content:
        await message.channel.send("hi")
    await bot.process_commands(message)

or use a listener, which runs alongside the default instead of replacing it:

@bot.listen("on_message")
async def say_hi(message):
    if "hello" in message.content:
        await message.channel.send("hi")

3. Slash commands were never synced

Defining a slash command in code does not tell Discord it exists. You have to sync the command tree, once, after the commands are defined. Until then nothing shows up when you type /.

class MyBot(commands.Bot):
    async def setup_hook(self):
        await self.tree.sync()

If they still do not appear, fully restart your Discord client (Ctrl+R on desktop). More in slash commands in discord.py.

4. The bot cannot see or talk in that channel

Channel permission overrides beat server roles. A bot with Send Messages at server level can still be blocked in one channel. Check with the channel's Edit Channel → Permissions, or just try the command in another channel.

When a send fails for this reason discord.py raises discord.Forbidden with 50013 Missing Permissions or 50001 Missing Access. If you do not see that in your logs, read the next point.

5. Errors are being swallowed

By default discord.py prints command errors to the console. Two common ways to lose them:

  • A custom on_command_error that handles one case and ignores the rest. Make sure the last branch logs or re-raises.
  • Running the bot somewhere you are not looking at the output, such as a background terminal that closed.

Turn on logging so nothing is silent:

import logging
logging.basicConfig(level=logging.INFO)

6. Something is blocking the event loop

discord.py runs on asyncio. A single blocking call, such as time.sleep(10), requests.get() or a slow database query without await, freezes the entire bot while it runs. No commands, no events. If it blocks long enough you will see:

Shard ID None heartbeat blocked for more than 10 seconds.

Swap time.sleep for await asyncio.sleep, requests for aiohttp, and push unavoidable blocking work into a thread with await asyncio.to_thread(slow_function).

7. The cog never loaded

Commands in a cog only exist once the extension is loaded. In discord.py 2, load_extension is a coroutine and has to be awaited, usually in setup_hook:

async def setup_hook(self):
    await self.load_extension("cogs.fun")

Calling it without await produces a "coroutine was never awaited" warning and no commands. Also check the cog file has an async def setup(bot) at the bottom.

8. You are running two copies

If the same token is running in two places, say your laptop and a host, both copies receive every event. The usual symptom is double replies, but with slash commands the copy that answers first wins and the other logs an "Unknown interaction" error, which looks like random failures. Stop the one you do not want.

Still nothing?

Strip it back to the smallest bot that should work: one file, one command, default intents plus message content. If that responds, add your code back a piece at a time. If it does not, the problem is outside your code: the token belongs to a different application than the one you invited, or the bot was never invited to that server.

If you host with SnowServers, the troubleshooting doc lists the exact console messages for the problems that stop a bot starting at all.