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 channels map is present, only the channels it lists are permitted

  • check requireMention behavior and mention patterns

    The Control UI channel details and openclaw channels status issue a warning when the effective policy is allowlist yet no guilds are configured. Add your server under channels.discord.guilds, or to the account's guilds map when that is overridden. An explicit channels.discord.accounts.default.guilds map 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 allowlist
  • requireMention placed in the wrong spot (it must sit under channels.discord.guilds or a channel entry)
  • sender rejected by the guild/channel users allowlist

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 pairing mode

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 minute

    Set 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, and cooldownSeconds keys.

{
  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=true is 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 detected
    • discord 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
1,091 words · updated Oct 26, 2026