Discord bot not responding but still running: how to catch it

Your bot shows as online in Discord. The process is there in ps. And it answers nothing.

This is the failure that gets past every restart-if-it-dies setup, because nothing died. Here is how to detect it and what to do about it.

Why your supervisor misses this

launchd with KeepAlive, systemd with Restart=always, pm2, a while loop that checks for the process — they all watch the same thing. Is the process there?

When a session expires, a daily quota runs out, or a request hangs on a socket with no timeout, the process is still there. It holds memory, it shows up in the process list, and the supervisor is satisfied. The bot is dead in every way that matters and nothing will restart it.

Signals that lie to you

Before building a check, know which signals are useless here.

A bot that is alive, supervised, and not working
  • Discord shows the bot online. The gateway connection can outlive the part of your code that handles messages
  • The process exists. That is exactly the thing that is wrong
  • launchctl list shows a PID. Same problem
  • Logs are quiet. A stuck bot usually stops logging, and "no errors" reads like "no problems"
  • A screen dump is empty. If your bot draws a text interface, screen -X hardcopy writes an empty file. We tried to use it for health checks and wasted time on it

Watch work, not life

The check that works is the boring one. Have the bot record when it last did something real.

Not when it started. Not when it received a message. When it finished handling one.

from pathlib import Path
import time

HEARTBEAT = Path("/tmp/mybot.alive")

async def on_message(msg):
    ...                      # handle the command
    await msg.channel.send(reply)
    HEARTBEAT.write_text(str(int(time.time())))   # only after a real reply

Then a separate script decides whether that timestamp is too old.

now=$(date +%s)
last=$(cat /tmp/mybot.alive 2>/dev/null || echo 0)
if [ $((now - last)) -gt 900 ]; then
  pkill -f mybot.py          # let the supervisor bring it back
fi

Killing it is the whole fix. You do not need restart logic — whatever already restarts a dead process will handle it. You are just converting an invisible failure into a visible one.

Choosing the threshold

Too short and you restart a bot that is merely idle. Too long and you are down for hours.

The number depends on traffic, not on taste. If your bot handles something every few minutes, fifteen minutes is generous. If it goes quiet overnight, a timestamp alone will produce false restarts — in that case have the bot do a cheap internal task on a timer and stamp that, so the heartbeat reflects the bot being capable, not being used.

Keep the watcher light

This one cost us eight hours.

Our watchdog checked connectivity before restarting the bot, and that check fetched a page — the whole 170KB body. On a slow connection it exceeded its own timeout, decided the network was down, and refused to restart. The bot was dead the entire time and the watchdog was the reason it stayed dead.

Switching to a HEAD request took it to 0.5 seconds.

If a watchdog can fail, it will fail at the worst time. Keep every check in it cheap and give each one a timeout shorter than the loop interval.

A quick audit

Answer these about your own setup:

1. If the process froze right now, what would notice? If the answer is "me, eventually", you have this problem

2. Does your health check look at anything other than the process existing?

3. Can your watchdog itself hang? What is its slowest operation?

Related

  • The setup this runs on: Run a Discord bot 24/7 for free on a computer you already own
  • Environment traps when starting from a supervisor: mac environment variables that autostart does not have

Every number here is from a bot that has been running on the same machine for 34 days.

Related posts

Comments

Popular posts from this blog

npm command not found on Windows: fix the PATH

Claude CLI 401 Unauthorized Refresh Token Issue

Tailscale without sudo: what userspace mode actually costs you