Skip to content
Current coupon:10% offUse it in your order

Discord bot: logs, restarts and fixing errors

Where to find your bot's output, how automatic restart works, how to catch errors so the bot doesn't crash, and what the most common messages mean, from Cannot find module to Missing Permissions.

  • beginner
  • ~15 min
  • Updated 2 Oct 2026

When your bot doesn't work, the answer is almost always in its output. We'll show you where to find the output, how automatic restart works, how to catch errors in code and what the most common messages mean.

What you'll need

  • A Discord bot at StormHost (or on your own VPS)
  • Access to the customer area
  • A bot written in Node.js; the code examples are for discord.js version 14
  • About 15 minutes

Where to find the logs#

In the customer area, open My services, click your bot and stay on the Console tab.

  • The console shows everything the bot prints, live: console.log() and console.info() go to standard output, console.error() and console.warn() to the error output. When the application crashes, you'll see its output here (an error with a list of files and lines, called a stack trace).
  • Below the console, you can see the current CPU, RAM, disk and network usage.
  • The Start, Restart and Stop buttons are in the Console tab, above the console itself.

Streamer mode

The bot console hides IP addresses in the output only when streamer mode is on. It won't hide your token or passwords for you, though. Never print them to the console and never share a screenshot that shows them. More in the Customer area guide.

Logging to a file#

The console only shows the latest part of the output. If you want to look up what happened last night, write important events to a file as well. A simple helper for CommonJS (require):

logger.jsJavaScript
const fs = require('node:fs');
const path = require('node:path');
const util = require('node:util');

const slozka = path.join(__dirname, 'logs');
fs.mkdirSync(slozka, { recursive: true });
const soubor = fs.createWriteStream(path.join(slozka, 'bot.log'), { flags: 'a' });

function zapis(uroven, ...casti) {
  const radek = `[${new Date().toISOString()}] ${uroven} ${util.format(...casti)}`;
  (uroven === 'ERROR' ? console.error : console.log)(radek);
  soubor.write(radek + '\n');
}

module.exports = {
  info: (...c) => zapis('INFO', ...c),
  warn: (...c) => zapis('WARN', ...c),
  error: (...c) => zapis('ERROR', ...c),
};
PartWhat it does
fs.mkdirSync(slozka, { recursive: true })Creates the logs folder next to the file if it doesn't exist.
createWriteStream(…, { flags: 'a' })Opens the file for appending to the end (a = append); old entries stay.
new Date().toISOString()The time of the event in UTC, for example 2026-10-01T18:04:12.345Z.
util.format(...casti)Joins the parts of the message the same way console.log does. For an error it also writes the list of lines (stack trace), for an object its contents.
console.error / console.logThe line also shows up in the console.
soubor.write(radek + '\n')Writes the line to logs/bot.log.

Usage in the bot: const log = require('./logger'); and then log.info('Bot is ready') or log.error('Command failed:', chyba). You can download logs/bot.log in the Files tab. Delete or empty it from time to time so it doesn't fill up the disk.

Automatic restart#

When the bot crashes (the process exits with an error), it starts again automatically. You often won't even notice a short outage, but you'll see the error in the console output.

  • If the bot keeps crashing right after startup, the error is in your code or settings and a restart won't fix it. Read the first error in the output, fix it and start the bot with the Start button.
  • Don't use process.exit() in your code as a normal way to stop the bot. The bot is meant to run continuously.
  • Use Restart after uploading new code or changing variables in the Settings tab; the changes only take effect after a restart.

Catching errors so the bot doesn't crash#

Node.js exits the process on every error that nobody catches. That includes a rejected Promise without a catch (the default behavior since Node.js 15). One error in one command can bring down the whole bot. Add these listeners to the main file (for example index.js):

index.jsJavaScript
const { Client, Events, GatewayIntentBits } = require('discord.js');

const client = new Client({ intents: [GatewayIntentBits.Guilds] });

client.on(Events.Error, (chyba) => console.error('discord.js client error:', chyba));
client.on(Events.Warn, (zprava) => console.warn('discord.js warning:', zprava));
client.on(Events.ShardError, (chyba) => console.error('Discord connection error:', chyba));

process.on('unhandledRejection', (duvod) => {
  console.error('Unhandled promise rejection:', duvod);
});

process.on('uncaughtException', (chyba) => {
  console.error('Uncaught exception, the bot will exit:', chyba);
  process.exit(1);
});
ListenerWhen it runsWhy you want it
Events.ErrorThe discord.js client reports an error.Without a listener, the error event exits the process.
Events.WarnThe client reports a warning.It warns you about a problem before something crashes.
Events.ShardErrorAn error in the connection to the Discord gateway (websocket).discord.js reconnects on its own; you'll see why it happened.
unhandledRejectionA Promise failed and nobody caught the error (a missing await inside try, or a missing .catch()).The bot won't crash because of one forgotten error, and you'll see where it came from.
uncaughtExceptionA synchronous error that nothing caught.Prints the error and exits the bot. It isn't safe to continue after such an error; automatic restart starts the bot again in a clean state.

Catch errors in individual commands right in the interaction handler so the user gets a reply and the bot keeps running:

JavaScript
const { Events, MessageFlags } = require('discord.js');

client.on(Events.InteractionCreate, async (interaction) => {
  if (!interaction.isChatInputCommand()) return;

  try {
    await zpracujPrikaz(interaction);
  } catch (chyba) {
    console.error(`Command /${interaction.commandName} failed:`, chyba);
    const odpoved = { content: 'There was an error while running this command.', flags: MessageFlags.Ephemeral };
    if (interaction.replied || interaction.deferred) {
      await interaction.followUp(odpoved).catch(console.error);
    } else {
      await interaction.reply(odpoved).catch(console.error);
    }
  }
});
  • isChatInputCommand() – handles only slash commands.
  • zpracujPrikaz(interaction) – your function with the command logic.
  • try { … } catch (chyba) { … } – an error in the command ends up in catch instead of crashing the bot.
  • MessageFlags.Ephemeral – only the user who ran the command sees the reply.
  • interaction.replied || interaction.deferred – if the bot has already replied or deferred the reply, it has to send the next message with followUp, otherwise with reply.
  • .catch(console.error) – if even the error reply fails, it just gets printed.

Memory#

The bot has as much RAM as your plan provides. When it runs out, it crashes (and restarts automatically) or slows down significantly. A typical message on a crash:

Outputtext
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

Node.js prints this message itself when it runs out of memory for JavaScript objects. When the system kills the process because of the memory limit, the output can end without any error message. In both cases, it helps to find out what's using the memory (see below).

Keep an eye on usage below the console, or have the bot print it:

JavaScript
setInterval(() => {
  const mb = (b) => Math.round(b / 1024 / 1024);
  const m = process.memoryUsage();
  console.log(`Memory: total ${mb(m.rss)} MB, heap ${mb(m.heapUsed)}/${mb(m.heapTotal)} MB`);
}, 10 * 60 * 1000);
  • process.memoryUsage() – the process's current usage; rss is the total memory, heapUsed the memory used by JavaScript objects.
  • 10 * 60 * 1000 – prints every 10 minutes (the value is in milliseconds).

If memory keeps growing, the most common causes are:

CauseFix
discord.js keeps messages, members and other objects in memory (cache). On large servers that's tens of thousands of items.Limit the cache and let old items be swept (example below). Don't enable the GuildMembers intent unless you need it, and don't fetch all members (guild.members.fetch()) without a reason.
Your own arrays or maps that only ever grow (history, cooldowns, cached results).Delete old items, or store the data in a database.
setInterval or event listeners added over and over (for example inside a command handler).Register listeners once at startup.

Limiting the cache in discord.js:

JavaScript
const { Client, GatewayIntentBits, Options } = require('discord.js');

const client = new Client({
  intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
  makeCache: Options.cacheWithLimits({
    ...Options.DefaultMakeCacheSettings,
    MessageManager: 50,
  }),
  sweepers: {
    ...Options.DefaultSweeperSettings,
    messages: { interval: 3_600, lifetime: 1_800 },
  },
});
  • makeCache: Options.cacheWithLimits({ … }) – limits the cache size; MessageManager: 50 = at most 50 messages per channel.
  • ...Options.DefaultMakeCacheSettings – keeps the other caches at their default settings.
  • sweepers.messages – every hour (interval: 3_600 seconds), removes messages older than 30 minutes (lifetime: 1_800) from the cache.

Don't limit some caches

The discord.js documentation warns that limiting the cache for GuildManager, ChannelManager, GuildChannelManager, RoleManager and PermissionOverwriteManager isn't supported and breaks library features.

If the bot really needs more memory after optimizing, move to a bigger plan in the Change plan tab. You only pay the difference until the end of the period; the steps are in the Renewal and upgrade article.

Bot on your own VPS#

If you run the bot on a VPS, set it up as a systemd service. That gives you automatic restarts and startup after a VPS reboot, and the logs are stored in the system journal (journalctl -u <SERVICE_NAME> -f). Steps: Run an app 24/7 with systemd.

Troubleshooting#

Error: Cannot find module 'discord.js'#

The packages aren't installed (the node_modules folder is missing, or the package isn't in it). Check that the project root has a package.json with discord.js in its dependencies. How packages are installed on our servers is described in the Uploading and starting the bot guide.

Error: Cannot find module '…/index.js' or './commands/ping.js'#

Node.js didn't find the file at that path:

  • The main file has a different name, or is in a different subfolder than the startup settings expect.
  • Letter case: Windows doesn't distinguish it, but the Linux server does. require('./Commands/ping.js') won't find the commands folder on the server. Make the names match.
  • A relative path is resolved from the file that contains the require, not from the folder the bot starts in. Use path.join(__dirname, …).

Error [ERR_REQUIRE_ESM]: require() of ES Module … not supported#

You're loading a package with require() that exists only as an ES module (for example newer versions of some libraries). Fixes:

  • Switch the project to ES modules (import), see the next error.
  • Or load the package dynamically: const { default: library } = await import('package-name');.
  • Or use an older version of the package that still supports CommonJS.

Newer Node.js versions (20.19 and 22.12 and later) can load ES modules with require() and report this error only on older versions. For modules with top-level await, they report ERR_REQUIRE_ASYNC_MODULE. To find out which Node.js version your bot uses, see Node.js version.

ReferenceError: require is not defined in ES module scope, you can use import instead#

The project is set up as ES modules ("type": "module" in package.json, or a file with the .mjs extension), but the code uses require(). Either rewrite require to import (import { Client } from 'discord.js';), or remove "type": "module" from package.json. __dirname doesn't work in ES modules either; use import.meta.dirname (Node.js 20.11 and newer).

SyntaxError: Cannot use import statement outside a module#

The opposite of the previous error: the code uses import, but the project is CommonJS (typically "type": "commonjs" in package.json). Add "type": "module" to package.json, or use require(). Don't mix the two in one project.

SyntaxError: Unexpected token (for example '?' or '.')#

The code uses syntax that the Node.js version on the server doesn't support yet. Find out the version (console.log(process.version) at the start of the bot) and compare it with the requirements of your libraries: discord.js 14 requires Node.js 18 or newer. Steps: Node.js version.

Error: Used disallowed intents#

The bot requests a privileged intent that isn't enabled in the Discord Developer Portal. Open the Developer Portal (opens in a new window), your application and the Bot section, and under Privileged Gateway Intents turn on what the bot actually uses (Presence Intent, Server Members Intent, Message Content Intent). Or remove the intent from the code. Details: Token and intents.

An invalid token was provided. (TokenInvalid)#

The token is wrong, has been invalidated (after Reset Token in the Developer Portal) or never got loaded into the bot (the environment variable is empty, the .env file is missing or isn't loaded). To check, print only the token's length, never the whole token: console.log('Token length:', process.env.DISCORD_TOKEN?.length);. Use the variable name you have in your code. Steps: Token and intents.

DiscordAPIError[50013]: Missing Permissions#

The bot doesn't have permission for the action (sending a message, adding a role, deleting a message, banning):

  • Check the bot's roles in the Discord server settings and the permissions in that channel (a channel can override permissions).
  • Role order: the bot can only manage roles that are below its highest role in the role list. Drag the bot's role higher.
  • The bot can't modify the server owner in any way.

DiscordAPIError[50001]: Missing Access#

The bot can't see the channel or the server: it doesn't have the View Channel permission in the channel, isn't on the server at all, or you're registering commands for a server with the wrong ID (the bot isn't on it). If someone invited the bot a long time ago with only the bot scope and command registration keeps failing, invite it again with a link that has the bot and applications.commands scopes.

DiscordAPIError[10062]: Unknown interaction#

An interaction (slash command, button) has to be answered within 3 seconds. When a command takes longer (database, API), call await interaction.deferReply(); right at the start and send the result later with interaction.editReply(…). A deferred reply stays valid for 15 minutes.

The same error (or DiscordAPIError[40060]: Interaction has already been acknowledged) also happens when two copies of the bot are running with the same token, for example one on our servers and another on your computer. Both try to reply. Keep only one running.

ECONNRESET, ETIMEDOUT, EAI_AGAIN#

A temporary network outage or a dropped connection to Discord. discord.js reconnects to the gateway on its own and retries failed REST API requests several times. If the error keeps coming back over a long time, check our service status and Discord's status (discordstatus.com (opens in a new window)).

The bot is slow and the output shows 429 or a rate limit#

Discord limits the number of requests (globally 50 per second per bot, plus other limits for individual actions). When a limit is exceeded, discord.js automatically waits and sends the request later, which is why the bot “slows down”. Don't send messages, roles or edits in bulk in a loop.

Repeated invalid requests = IP address block

When a bot sends 10,000 invalid requests within 10 minutes (401, 403 or 429 responses), Discord temporarily blocks the IP address. This is typically caused by a loop that keeps trying an action without permission or with a wrong token. Fix such an error right away, not with a restart.

The bot is online but doesn't respond to messages#

To read message content (messageCreate and message.content), the bot needs the GuildMessages and MessageContent intents in code and Message Content Intent enabled in the Developer Portal. Without it, message.content is empty. For commands, use slash commands instead, which don't need this intent (first discord.js bot).

The bot is offline#

  1. Look at the Console tab: is the bot running, or is it stopped? If it's stopped, start it with the Start button.
  2. If it starts and crashes right away, read the first error in the output and look it up above.
  3. If it runs without errors but is offline, check the token (after a token reset, the old one stops working and the bot can't log in).

None of this helped: when to contact support#

Contact us (contacts) when:

  • the console doesn't load or the Start and Restart buttons don't respond,
  • the bot won't start even with minimal code that works elsewhere,
  • you need a different Node.js version or more memory.

Send us the service name or ID, the time of the problem and the exact error message (the text copied from the console). Never send the bot token.

Summary

  • You'll find the bot's output in the Console tab; for history, write it to a file as well.
  • The bot restarts on its own after a crash; if it crashes right after startup, the fix is in the code.
  • The Events.Error and unhandledRejection listeners and try/catch in commands keep one error from crashing the bot.
  • Used disallowed intents and TokenInvalid are fixed in the Developer Portal, Missing Permissions in the roles on the Discord server.
  • Unknown interaction = a late reply (use deferReply) or two running copies of the bot.

Search the guides