Discord Troubleshooting: Fix Common Account and Gateway Errors
Symptom-based checks for a misbehaving Discord account. Covers blocked guild messages, intent errors, gateway timeouts, bot loops, voice STT drops, and startup log diagnosis.
Read this when
- The Discord bot does not reply in a guild channel or DM
- Discord gateway timeouts, duplicate replies, or bot-to-bot loops appear
- Voice speech-to-text drops with a decryption failure
- The Discord plugin fails during register with ERR_MODULE_NOT_FOUND
Symptom-based checks for a Discord account that misbehaves.
Every account start writes [account] starting account (reason: ...) at info level.
The reason field separates startup, manual starts, automatic retries, health recovery, config/plugin/secret reloads, and host-thaw recovery. A Discord socket reconnect or resume occurs within the lifetime of the existing account and does not emit this line. An event-loop stall label names work while its labeled operation is running; on its own it does not prove that an account restarted.
Troubleshooting
Plugin fails during register with ERR_MODULE_NOT_FOUND
On OpenClaw 2026.9.33 and later, the Discord channel may fail to start on each Gateway restart, with a log line such as:
plugin failed during register: runtime dependencies are missing for externally distributed plugin discord; reinstall or update the plugin package, then restart the Gateway. (Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'mdast-util-to-string' imported from .../plugin-captures/.../node_modules/@openclaw/discord/node_modules/mdast-util-from-markdown/lib/index.js)
Reinstalling the plugin will not solve this. It occurs when the Gateway's Node process is running with --preserve-symlinks or NODE_PRESERVE_SYMLINKS=1. Take the flag out of the Gateway service, then restart the Gateway. For how to inspect the service and apply the workaround, see Nested dependency missing under preserve-symlinks.
Used disallowed intents or bot sees no guild messages
- enable Message Content Intent
- enable Server Members Intent when user/member resolution matters to you
- restart gateway after intents are changed
Guild messages blocked unexpectedly
-
check
groupPolicy -
check the guild allowlist under
channels.discord.guilds -
when a guild
channelsmap is present, only the channels it lists are permitted -
check
requireMentionbehavior and mention patternsThe Control UI channel details and
openclaw channels statusissue a warning when the effective policy isallowlistyet no guilds are configured. Add your server underchannels.discord.guilds, or to the account'sguildsmap when that is overridden. An explicitchannels.discord.accounts.default.guildsmap likewise takes precedence over the top-level map, even if the account map is empty.When status reports a deferred configuration reload, let active work finish and then refresh. A channel stop/start that succeeds does not apply configuration that has not been published. The warning separates waiting to publish configuration from channel work deferred after publication; connection health by itself does not prove a policy change took effect.
Helpful checks:
openclaw doctor
openclaw channels status --probe
openclaw logs --follow
Require mention false but still blocked
Frequent causes:
groupPolicy="allowlist"with no matching guild/channel allowlistrequireMentionplaced in the wrong spot (it must sit underchannels.discord.guildsor a channel entry)- sender rejected by the guild/channel
usersallowlist
Long-running Discord turns or duplicate replies
Logs you will typically see:
Slow listener detected ...stuck session: sessionKey=agent:...:discord:... state=processing ...
No channel-owned timeout is applied by Discord to queued agent turns. Message listeners hand off right away, and queued Discord runs keep per-session ordering until the session/tool/runtime lifecycle finishes or aborts the work.
Gateway metadata lookup timeout warnings
Before connecting, OpenClaw fetches Discord /gateway/bot metadata. On transient failures it falls back to Discord's default gateway URL, and those failures are rate-limited in the logs.
The metadata timeout defaults to 30 seconds. OPENCLAW_DISCORD_GATEWAY_INFO_TIMEOUT_MS can override it for unusual host environments.
Gateway READY timeout restarts
During startup, and again after runtime reconnects, OpenClaw waits for Discord's gateway READY event. Setups with multiple accounts and staggered startup may need a longer startup READY window than the default.
Startup waits 15 seconds, while runtime reconnects wait 30 seconds. OPENCLAW_DISCORD_READY_TIMEOUT_MS and OPENCLAW_DISCORD_RUNTIME_READY_TIMEOUT_MS stay available for unusual host environments.
Permissions audit mismatches
channels status --probe permission checks work only with numeric channel IDs.
Runtime matching may still work if you use slug keys, but check cannot fully verify permissions.
DM and pairing issues
- DM disabled:
channels.discord.dm.enabled=false - DM policy disabled:
channels.discord.dmPolicy="disabled"(legacy:channels.discord.dm.policy) - waiting for pairing approval in
pairingmode
Bot to bot loops
Under normal mention and access rules, bot-authored messages are accepted by default.
Keep mention and allowlist rules suitable for the room. Set channels.discord.allowBots=false to turn off bot-triggered turns, or channels.discord.allowBots="mentions" to accept only bot messages that mention the bot. These settings do not hide bot-authored history you can access or reply context a human selected.
In "mentions" mode, reply-ping metadata on its own does not count. A bot reply needs an active native mention, or a configured text/transcript mention outside Markdown code.
OpenClaw also provides shared bot loop protection. When allowBots allows bot-authored messages to reach dispatch, Discord converts the inbound event into (account, channel, bot pair) facts, and the generic pair guard suppresses the pair once it passes the configured event budget. The guard limits fast two-bot loops; exchanges that stay under the budget may continue. Human messages are unaffected, as are one-shot bot replies that remain under the budget.
Default settings (active whenever bot-authored messages are admitted):
-
maxEventsPerWindow: 20-- a bot pair can exchange 20 messages inside the sliding window -
windowSeconds: 60-- length of the sliding window -
cooldownSeconds: 60-- after the budget trips, each further bot-to-bot message in either direction is dropped for one minuteSet the shared default once under
channels.defaults.botLoopProtection, then override Discord where a legitimate workflow needs more headroom. Precedence order: -
channels.discord.accounts.<account>.botLoopProtection -
channels.discord.botLoopProtection -
channels.defaults.botLoopProtection -
built-in defaults
Discord relies on the generic
maxEventsPerWindow,windowSeconds, andcooldownSecondskeys.
{
channels: {
defaults: {
botLoopProtection: {
maxEventsPerWindow: 20,
windowSeconds: 60,
cooldownSeconds: 60,
},
},
discord: {
// Optional Discord-wide override. Account blocks override individual
// fields and inherit omitted fields from here.
botLoopProtection: {
maxEventsPerWindow: 4,
},
accounts: {
alpha: {
// Alpha listens to other bots only when they mention it.
allowBots: "mentions",
},
bravo: {
// Bravo listens to all bot-authored Discord messages.
allowBots: true,
mentionAliases: {
// Lets Bravo write an Alpha Discord mention with the configured user id.
Alpha: "ALPHA_DISCORD_USER_ID",
},
botLoopProtection: {
// Allow up to five messages per minute before suppressing the pair.
maxEventsPerWindow: 5,
windowSeconds: 60,
cooldownSeconds: 90,
},
},
},
},
},
}
Voice STT drops with DecryptionFailed(...)
- use OpenClaw 2026.2.24 or later (
openclaw update), the version in which Discord voice receive recovery logic was introduced (#25861) - verify that
channels.discord.voice.daveEncryption=trueis set (this is the default) - begin with
channels.discord.voice.decryptionFailureTolerance=24(the upstream default) and adjust it only when necessary - monitor the logs for:
discord voice: DAVE decrypt failures detecteddiscord voice: repeated decrypt failures; attempting rejoin
- should failures persist after the automatic rejoin, gather logs and check them against the upstream DAVE receive history in discord.js #11419 and discord.js #11449