Ready for DMs and guild channels via the official Discord gateway.Documentation Index
Fetch the complete documentation index at: https://docs.openclaw.ai/llms.txt
Use this file to discover all available pages before exploring further.
Pairing
Slash commands
Channel troubleshooting
Quick setup
You will need to create a new application with a bot, add the bot to your server, and pair it to OpenClaw. We recommend adding your bot to your own private server. If you don’t have one yet, create one first (choose Create My Own > For me and my friends).Create a Discord application and bot
Enable privileged intents
- Message Content Intent (required)
- Server Members Intent (recommended; required for role allowlists and name-to-ID matching)
- Presence Intent (optional; only needed for presence updates)
Copy your bot token
Generate an invite URL and add the bot to your server
botapplications.commands
- View Channels Text Permissions
- Send Messages
- Read Message History
- Embed Links
- Attach Files
- Add Reactions (optional)
Enable Developer Mode and collect your IDs
- Click User Settings (gear icon next to your avatar) → Advanced → toggle on Developer Mode
- Right-click your server icon in the sidebar → Copy Server ID
- Right-click your own avatar → Copy User ID
Allow DMs from server members
Set your bot token securely (do not send it in chat)
openclaw gateway run process.
For managed service installs, run openclaw gateway install from a shell where DISCORD_BOT_TOKEN is present, or store the variable in ~/.openclaw/.env, so the service can resolve the env SecretRef after restart.
If your host is blocked or rate-limited by Discord’s startup application lookup, set the Discord application/client ID from the Developer Portal so startup can skip that REST call. Use channels.discord.applicationId for the default account, or channels.discord.accounts.<accountId>.applicationId when you run multiple Discord bots.Configure OpenClaw and pair
- Ask your agent
- CLI / config
“I already set my Discord bot token in config. Please finish Discord setup with User ID<user_id>and Server ID<server_id>.”
Approve first DM pairing
- Ask your agent
- CLI
“Approve this Discord pairing code: <CODE>”
DISCORD_BOT_TOKEN is only used for the default account.
If two enabled Discord accounts resolve to the same bot token, OpenClaw starts only one gateway monitor for that token. A config-sourced token wins over the default env fallback; otherwise the first enabled account wins and the duplicate account is reported disabled.
For advanced outbound calls (message tool/channel actions), an explicit per-call token is used for that call. This applies to send and read/probe-style actions (for example read/search/fetch/thread/pins/permissions). Account policy/retry settings still come from the selected account in the active runtime snapshot.Recommended: Set up a guild workspace
Once DMs are working, you can set up your Discord server as a full workspace where each channel gets its own agent session with its own context. This is recommended for private servers where it’s just you and your bot.Add your server to the guild allowlist
- Ask your agent
- Config
“Add my Discord Server ID <server_id> to the guild allowlist”
Allow responses without @mention
message tool, so the agent can lurk by default and only post when it decides a channel reply is useful.This means the selected model must reliably call tools. If Discord shows typing and the logs show token usage but no posted message, check the session log for assistant text with didSendViaMessagingTool: false. That means the model produced a private final answer instead of calling message(action=send). Switch to a stronger tool-calling model, or use the config below to restore legacy automatic final replies.- Ask your agent
- Config
“Allow my agent to respond on this server without having to be @mentioned”
#coding, #home, #research, or whatever fits your workflow.
Runtime model
- Gateway owns the Discord connection.
- Reply routing is deterministic: Discord inbound replies back to Discord.
- Discord guild/channel metadata is added to the model prompt as untrusted context, not as a user-visible reply prefix. If a model copies that envelope back, OpenClaw strips the copied metadata from outbound replies and from future replay context.
- By default (
session.dmScope=main), direct chats share the agent main session (agent:main:main). - Guild channels are isolated session keys (
agent:<agentId>:discord:channel:<channelId>). - Group DMs are ignored by default (
channels.discord.dm.groupEnabled=false). - Native slash commands run in isolated command sessions (
agent:<agentId>:discord:slash:<userId>), while still carryingCommandTargetSessionKeyto the routed conversation session. - Text-only cron/heartbeat announce delivery to Discord uses the final assistant-visible answer once. Media and structured component payloads remain multi-message when the agent emits multiple deliverable payloads.
Forum channels
Discord forum and media channels only accept thread posts. OpenClaw supports two ways to create them:- Send a message to the forum parent (
channel:<forumId>) to auto-create a thread. The thread title uses the first non-empty line of your message. - Use
openclaw message thread createto create a thread directly. Do not pass--message-idfor forum channels.
channel:<threadId>).
Interactive components
OpenClaw supports Discord components v2 containers for agent messages. Use the message tool with acomponents payload. Interaction results are routed back to the agent as normal inbound messages and follow the existing Discord replyToMode settings.
Supported blocks:
text,section,separator,actions,media-gallery,file- Action rows allow up to 5 buttons or a single select menu
- Select types:
string,user,role,mentionable,channel
components.reusable=true to allow buttons, selects, and forms to be used multiple times until they expire.
To restrict who can click a button, set allowedUsers on that button (Discord user IDs, tags, or *). When configured, unmatched users receive an ephemeral denial.
The /model and /models slash commands open an interactive model picker with provider, model, and compatible runtime dropdowns plus a Submit step. /models add is deprecated and now returns a deprecation message instead of registering models from chat. The picker reply is ephemeral and only the invoking user can use it. Discord select menus are limited to 25 options, so add provider/* entries to agents.defaults.models when you want the picker to show dynamically discovered models only for selected providers such as openai-codex or vllm.
File attachments:
fileblocks must point to an attachment reference (attachment://<filename>)- Provide the attachment via
media/path/filePath(single file); usemedia-galleryfor multiple files - Use
filenameto override the upload name when it should match the attachment reference
- Add
components.modalwith up to 5 fields - Field types:
text,checkbox,radio,select,role-select,user-select - OpenClaw adds a trigger button automatically
Access control and routing
- DM policy
- Access groups
- Guild policy
- Mentions and group DMs
channels.discord.dmPolicy controls DM access. channels.discord.allowFrom is the canonical DM allowlist.pairing(default)allowlistopen(requireschannels.discord.allowFromto include"*")disabled
pairing mode).Multi-account precedence:channels.discord.accounts.default.allowFromapplies only to thedefaultaccount.- For one account,
allowFromtakes precedence over legacydm.allowFrom. - Named accounts inherit
channels.discord.allowFromwhen their ownallowFromand legacydm.allowFromare unset. - Named accounts do not inherit
channels.discord.accounts.default.allowFrom.
channels.discord.dm.policy and channels.discord.dm.allowFrom still read for compatibility. openclaw doctor --fix migrates them to dmPolicy and allowFrom when it can do so without changing access.DM target format for delivery:user:<id><@id>mention
allowFrom are treated as user DM targets for compatibility.Role-based agent routing
Usebindings[].match.roles to route Discord guild members to different agents by role ID. Role-based bindings accept role IDs only and are evaluated after peer or parent-peer bindings and before guild-only bindings. If a binding also sets other match fields (for example peer + guildId + roles), all configured fields must match.
Native commands and command auth
commands.nativedefaults to"auto"and is enabled for Discord.- Per-channel override:
channels.discord.commands.native. commands.native=falseskips Discord slash-command registration and cleanup during startup. Previously registered commands may remain visible in Discord until you remove them from the Discord app.- Native command auth uses the same Discord allowlists/policies as normal message handling.
- Commands may still be visible in Discord UI for users who are not authorized; execution still enforces OpenClaw auth and returns “not authorized”.
ephemeral: true
Feature details
Reply tags and native replies
Reply tags and native replies
Live stream preview
Live stream preview
channels.discord.streaming takes off | partial | block | progress (default). progress keeps one editable status draft and updates it with tool progress until final delivery; the shared starter label is a rolling line, so it scrolls away like the rest once enough work appears. streamMode is a legacy runtime alias. Run openclaw doctor --fix to rewrite persisted config to the canonical key.Set channels.discord.streaming.mode to off to disable Discord preview edits. If Discord block streaming is explicitly enabled, OpenClaw skips the preview stream to avoid double-streaming.partialedits a single preview message as tokens arrive.blockemits draft-sized chunks (usedraftChunkto tune size and breakpoints, clamped totextChunkLimit).- Media, error, and explicit-reply finals cancel pending preview edits.
streaming.preview.toolProgress(defaulttrue) controls whether tool/progress updates reuse the preview message.- Tool/progress rows render as compact emoji + title + detail when available, for example
🛠️ Bash: run testsor🔎 Web Search: for "query". streaming.preview.commandText/streaming.progress.commandTextcontrols command/exec detail in compact progress lines:raw(default) orstatus(tool label only).
block streaming is explicitly enabled, OpenClaw skips the preview stream to avoid double-streaming.History, context, and thread behavior
History, context, and thread behavior
channels.discord.historyLimitdefault20- fallback:
messages.groupChat.historyLimit 0disables
channels.discord.dmHistoryLimitchannels.discord.dms["<user_id>"].historyLimit
- Discord threads route as channel sessions and inherit parent channel config unless overridden.
- Thread sessions inherit the parent channel’s session-level
/modelselection as a model-only fallback; thread-local/modelselections still take precedence and parent transcript history is not copied unless transcript inheritance is enabled. channels.discord.thread.inheritParent(defaultfalse) opts new auto-threads into seeding from the parent transcript. Per-account overrides live underchannels.discord.accounts.<id>.thread.inheritParent.- Message-tool reactions can resolve
user:<id>DM targets. guilds.<guild>.channels.<channel>.requireMention: falseis preserved during reply-stage activation fallback.
Thread-bound sessions for subagents
Thread-bound sessions for subagents
/focus <target>bind current/new thread to a subagent/session target/unfocusremove current thread binding/agentsshow active runs and binding state/session idle <duration|off>inspect/update inactivity auto-unfocus for focused bindings/session max-age <duration|off>inspect/update hard max age for focused bindings
session.threadBindings.*sets global defaults.channels.discord.threadBindings.*overrides Discord behavior.spawnSessionscontrols auto-create/bind threads forsessions_spawn({ thread: true })and ACP thread spawns. Default:true.defaultSpawnContextcontrols native subagent context for thread-bound spawns. Default:"fork".- Deprecated
spawnSubagentSessions/spawnAcpSessionskeys are migrated byopenclaw doctor --fix. - If thread bindings are disabled for an account,
/focusand related thread binding operations are unavailable.
Persistent ACP channel bindings
Persistent ACP channel bindings
bindings[]withtype: "acp"andmatch.channel: "discord"
/acp spawn codex --bind herebinds the current channel or thread in place and keeps future messages on the same ACP session. Thread messages inherit the parent channel binding.- In a bound channel or thread,
/newand/resetreset the same ACP session in place. Temporary thread bindings can override target resolution while active. spawnSessionsgates child thread creation/binding via--thread auto|here.
Reaction notifications
Reaction notifications
offown(default)allallowlist(usesguilds.<id>.users)
Ack reactions
Ack reactions
ackReaction sends an acknowledgement emoji while OpenClaw is processing an inbound message.Resolution order:channels.discord.accounts.<accountId>.ackReactionchannels.discord.ackReactionmessages.ackReaction- agent identity emoji fallback (
agents.list[].identity.emoji, else ”👀”)
- Discord accepts unicode emoji or custom emoji names.
- Use
""to disable the reaction for a channel or account.
Config writes
Config writes
/config set|unset flows (when command features are enabled).Disable:Gateway proxy
Gateway proxy
channels.discord.proxy.PluralKit support
PluralKit support
- allowlists can use
pk:<memberId> - member display names are matched by name/slug only when
channels.discord.dangerouslyAllowNameMatching: true - lookups use original message ID and are time-window constrained
- if lookup fails, proxied messages are treated as bot messages and dropped unless
allowBots=true
Outbound mention aliases
Outbound mention aliases
mentionAliases when agents need deterministic outbound mentions for known Discord users. Keys are handles without the leading @; values are Discord user IDs. Unknown handles, @everyone, @here, and mentions inside Markdown code spans are left unchanged.Presence configuration
Presence configuration
- 0: Playing
- 1: Streaming (requires
activityUrl) - 2: Listening
- 3: Watching
- 4: Custom (uses the activity text as the status state; emoji is optional)
- 5: Competing
autoPresence.healthyTextautoPresence.degradedTextautoPresence.exhaustedText(supports{reason}placeholder)
Approvals in Discord
Approvals in Discord
channels.discord.execApprovals.enabledchannels.discord.execApprovals.approvers(optional; falls back tocommands.ownerAllowFromwhen possible)channels.discord.execApprovals.target(dm|channel|both, default:dm)agentFilter,sessionFilter,cleanupAfterResolve
enabled is unset or "auto" and at least one approver can be resolved, either from execApprovals.approvers or from commands.ownerAllowFrom. Discord does not infer exec approvers from channel allowFrom, legacy dm.allowFrom, or direct-message defaultTo. Set enabled: false to disable Discord as a native approval client explicitly.For sensitive owner-only group commands such as /diagnostics and /export-trajectory, OpenClaw sends approval prompts and final results privately. It tries Discord DM first when the invoking owner has a Discord owner route; if that is not available, it falls back to the first available owner route from commands.ownerAllowFrom, such as Telegram.When target is channel or both, the approval prompt is visible in the channel. Only resolved approvers can use the buttons; other users receive an ephemeral denial. Approval prompts include the command text, so only enable channel delivery in trusted channels. If the channel ID cannot be derived from the session key, OpenClaw falls back to DM delivery.Discord also renders the shared approval buttons used by other chat channels. The native Discord adapter mainly adds approver DM routing and channel fanout.
When those buttons are present, they are the primary approval UX; OpenClaw
should only include a manual /approve command when the tool result says
chat approvals are unavailable or manual approval is the only path.
If the Discord native approval runtime is not active, OpenClaw keeps the
local deterministic /approve <id> <decision> prompt visible. If the
runtime is active but a native card cannot be delivered to any target,
OpenClaw sends a same-chat fallback notice with the exact /approve
command from the pending approval.Gateway auth and approval resolution follow the shared Gateway client contract (plugin: IDs resolve through plugin.approval.resolve; other IDs through exec.approval.resolve). Approvals expire after 30 minutes by default.See Exec approvals.Tools and action gates
Discord message actions include messaging, channel admin, moderation, presence, and metadata actions. Core examples:- messaging:
sendMessage,readMessages,editMessage,deleteMessage,threadReply - reactions:
react,reactions,emojiList - moderation:
timeout,kick,ban - presence:
setPresence
event-create action accepts an optional image parameter (URL or local file path) to set the scheduled event cover image.
Action gates live under channels.discord.actions.*.
Default gate behavior:
| Action group | Default |
|---|---|
| reactions, messages, threads, pins, polls, search, memberInfo, roleInfo, channelInfo, channels, voiceStatus, events, stickers, emojiUploads, stickerUploads, permissions | enabled |
| roles | disabled |
| moderation | disabled |
| presence | disabled |
Components v2 UI
OpenClaw uses Discord components v2 for exec approvals and cross-context markers. Discord message actions can also acceptcomponents for custom UI (advanced; requires constructing a component payload via the discord tool), while legacy embeds remain available but are not recommended.
channels.discord.ui.components.accentColorsets the accent color used by Discord component containers (hex).- Set per account with
channels.discord.accounts.<id>.ui.components.accentColor. embedsare ignored when components v2 are present.
Voice
Discord has two distinct voice surfaces: realtime voice channels (continuous conversations) and voice message attachments (the waveform preview format). The gateway supports both.Voice channels
Setup checklist:- Enable Message Content Intent in the Discord Developer Portal.
- Enable Server Members Intent when role/user allowlists are used.
- Invite the bot with
botandapplications.commandsscopes. - Grant Connect, Speak, Send Messages, and Read Message History in the target voice channel.
- Enable native commands (
commands.nativeorchannels.discord.commands.native). - Configure
channels.discord.voice.
/vc join|leave|status to control sessions. The command uses the account default agent and follows the same allowlist and group policy rules as other Discord commands.
voice.ttsoverridesmessages.ttsforstt-ttsvoice playback only. Realtime modes usevoice.realtime.voice.voice.modecontrols the conversation path. The default isagent-proxy: a realtime voice front end handles turn timing, interruption, and playback, delegates substantive work to the routed OpenClaw agent throughopenclaw_agent_consult, and treats the result like a typed Discord prompt from that speaker.stt-ttskeeps the older batch STT plus TTS flow.bidilets the realtime model converse directly while exposingopenclaw_agent_consultfor the OpenClaw brain.voice.agentSessioncontrols which OpenClaw conversation receives voice turns. Leave it unset for the voice channel’s own session, or set{ mode: "target", target: "channel:<text-channel-id>" }to make the voice channel act as the microphone/speaker extension of an existing Discord text channel session such as#maintainers.voice.modeloverrides the OpenClaw agent brain for Discord voice responses and realtime consults. Leave it unset to inherit the routed agent model. It is separate fromvoice.realtime.model.agent-proxyroutes speech throughdiscord-voice, which preserves normal owner/tool authorization for the speaker and target session but hides the agentttstool because Discord voice owns playback. By default,agent-proxygives the consult full owner-equivalent tool access for owner speakers (voice.realtime.toolPolicy: "owner") and strongly prefers consulting the OpenClaw agent before substantive answers (voice.realtime.consultPolicy: "always"). In that defaultalwaysmode, the realtime layer does not auto-speak filler before the consult answer; it captures and transcribes speech, then speaks the routed OpenClaw answer. If multiple forced consult answers finish while Discord is still playing the first answer, later exact-speech answers are queued until playback idles instead of replacing speech mid-sentence.- In
stt-ttsmode, STT usestools.media.audio;voice.modeldoes not affect transcription. - In realtime modes,
voice.realtime.provider,voice.realtime.model, andvoice.realtime.voiceconfigure the realtime audio session. For OpenAI Realtime 2 plus the Codex brain, usevoice.realtime.model: "gpt-realtime-2"andvoice.model: "openai-codex/gpt-5.5". - The OpenAI realtime provider accepts current Realtime 2 event names and legacy Codex-compatible aliases for output audio and transcript events, so compatible provider snapshots can drift without dropping assistant audio.
voice.realtime.bargeIncontrols whether Discord speaker-start events interrupt active realtime playback. If unset, it follows the realtime provider’s input-audio interruption setting.voice.realtime.minBargeInAudioEndMscontrols the minimum assistant playback duration before an OpenAI realtime barge-in truncates audio. Default:250. Set0for immediate interruption in low-echo rooms, or raise it for echo-heavy speaker setups.- For an OpenAI voice on Discord playback, set
voice.tts.provider: "openai"and choose a Text-to-speech voice undervoice.tts.openai.voiceorvoice.tts.providers.openai.voice.cedaris a good masculine-sounding choice on the current OpenAI TTS model. - Per-channel Discord
systemPromptoverrides apply to voice transcript turns for that voice channel. - Voice transcript turns derive owner status from Discord
allowFrom(ordm.allowFrom); non-owner speakers cannot access owner-only tools (for examplegatewayandcron). - Discord voice is opt-in for text-only configs; set
channels.discord.voice.enabled=true(or keep an existingchannels.discord.voiceblock) to enable/vccommands, the voice runtime, and theGuildVoiceStatesgateway intent. channels.discord.intents.voiceStatescan explicitly override voice-state intent subscription. Leave it unset for the intent to follow effective voice enablement.- If
voice.autoJoinhas multiple entries for the same guild, OpenClaw joins the last configured channel for that guild. voice.allowedChannelsis an optional residency allowlist. Leave it unset to allow/vc joininto any authorized Discord voice channel. When set,/vc join, startup auto-join, and bot voice-state moves are restricted to the listed{ guildId, channelId }entries. Set it to an empty array to deny all Discord voice joins. If Discord moves the bot outside the allowlist, OpenClaw leaves that channel and rejoins the configured auto-join target when one is available.voice.daveEncryptionandvoice.decryptionFailureTolerancepass through to@discordjs/voicejoin options.@discordjs/voicedefaults aredaveEncryption=trueanddecryptionFailureTolerance=24if unset.- OpenClaw defaults to the pure-JS
opusscriptdecoder for Discord voice receive. The optional native@discordjs/opuspackage is ignored by the repo pnpm install policy so normal installs, Docker lanes, and unrelated tests do not compile a native addon. Dedicated voice-performance hosts can opt in withOPENCLAW_DISCORD_OPUS_DECODER=nativeafter installing the native addon. voice.connectTimeoutMscontrols the initial@discordjs/voiceReady wait for/vc joinand auto-join attempts. Default:30000.voice.reconnectGraceMscontrols how long OpenClaw waits for a disconnected voice session to begin reconnecting before destroying it. Default:15000.- In
stt-ttsmode, voice playback does not stop just because another user starts speaking. To avoid feedback loops, OpenClaw ignores new voice capture while TTS is playing; speak after playback finishes for the next turn. Realtime modes forward speaker starts as barge-in signals to the realtime provider. - In realtime modes, echo from speakers into an open mic can look like barge-in and interrupt playback. For echo-heavy Discord rooms, set
voice.realtime.providers.openai.interruptResponseOnInputAudio: falseto keep OpenAI from auto-interrupting on input audio. Addvoice.realtime.bargeIn: trueif you still want Discord speaker-start events to interrupt active playback. The OpenAI realtime bridge ignores playback truncations shorter thanvoice.realtime.minBargeInAudioEndMsas likely echo/noise and logs them as skipped instead of clearing Discord playback. voice.captureSilenceGraceMscontrols how long OpenClaw waits after Discord reports a speaker has stopped before finalizing that audio segment for STT. Default:2500; raise this if Discord splits normal pauses into choppy partial transcripts.- When ElevenLabs is the selected TTS provider, Discord voice playback uses streaming TTS and starts from the provider response stream. Providers without streaming support fall back to the synthesized temp-file path.
- OpenClaw also watches receive decrypt failures and auto-recovers by leaving/rejoining the voice channel after repeated failures in a short window.
- If receive logs repeatedly show
DecryptionFailed(UnencryptedWhenPassthroughDisabled)after updating, collect a dependency report and logs. The bundled@discordjs/voiceline includes the upstream padding fix from discord.js PR #11449, which closed discord.js issue #11419. The operation was abortedreceive events are expected when OpenClaw finalizes a captured speaker segment; they are verbose diagnostics, not warnings.- Verbose Discord voice logs include a bounded one-line STT transcript preview for each accepted speaker segment, so debugging shows both the user side and the agent reply side without dumping unbounded transcript text.
- In
agent-proxymode, forced consult fallback skips likely incomplete transcript fragments such as text ending in...or a trailing connector likeand, plus obvious non-actionable closings like “be right back” or “bye”. Logs showforced agent consult skipped reason=...when this prevents a stale queued answer.
node-gyp source-build toolchain.
After installing the native addon, start the Gateway with:
discord voice: opus decoder: @discordjs/opus. Without the env opt-in, or if the native addon is missing or cannot load on the host, OpenClaw logs discord voice: opus decoder: opusscript and keeps receiving voice through the pure-JS fallback.
STT plus TTS pipeline:
- Discord PCM capture is converted to a WAV temp file.
tools.media.audiohandles STT, for exampleopenai/gpt-4o-mini-transcribe.- The transcript is sent through Discord ingress and routing while the response LLM runs with a voice-output policy that hides the agent
ttstool and asks for returned text, because Discord voice owns final TTS playback. voice.model, when set, overrides only the response LLM for this voice-channel turn.voice.ttsis merged overmessages.tts; streaming-capable providers feed the player directly, otherwise the resulting audio file is played in the joined channel.
voice.agentSession block, each voice channel gets its own routed OpenClaw session. For example, /vc join channel:234567890123456789 talks to the session for that Discord voice channel. The realtime model is only the voice front end; substantive requests are handed to the configured OpenClaw agent. If the realtime model produces a final transcript without calling the consult tool, OpenClaw forces the consult as a fallback so the default still behaves like talking to the agent.
Legacy STT plus TTS example:
agent-proxy mode the bot joins the configured voice channel, but OpenClaw agent turns use the target channel’s normal routed session and agent. The realtime voice session speaks the returned result back into the voice channel. The supervisor agent can still use normal message tools according to its tool policy, including sending a separate Discord message if that is the right action.
Useful target forms:
target: "channel:123456789012345678"routes through a Discord text channel session.target: "123456789012345678"is treated as a channel target.target: "dm:123456789012345678"ortarget: "user:123456789012345678"routes through that direct-message session.
bargeIn: true lets Discord speaker-start events and already-active speaker audio cancel active realtime responses before the next captured turn reaches OpenAI. Very early barge-in signals with audioEndMs below minBargeInAudioEndMs are treated as likely echo/noise and ignored so the model does not cut off at the first playback frame.
Expected voice logs:
- On join:
discord voice: joining ... voiceSession=... supervisorSession=... agentSessionMode=... voiceModel=... realtimeModel=... - On realtime start:
discord voice: realtime bridge starting ... autoRespond=false interruptResponse=false bargeIn=false minBargeInAudioEndMs=... - On speaker audio:
discord voice: realtime speaker turn opened ...,discord voice: realtime input audio started ... outputAudioMs=... outputActive=..., anddiscord voice: realtime speaker turn closed ... chunks=... discordBytes=... realtimeBytes=... interruptedPlayback=... - On skipped stale speech:
discord voice: realtime forced agent consult skipped reason=incomplete-transcript ...orreason=non-actionable-closing ... - On realtime response completion:
discord voice: realtime audio playback finishing reason=response.done ... audioMs=... chunks=... - On playback stop/reset:
discord voice: realtime audio playback stopped reason=... audioMs=... elapsedMs=... chunks=... - On realtime consult:
discord voice: realtime consult requested ... voiceSession=... supervisorSession=... question=... - On agent answer:
discord voice: agent turn answer ... - On queued exact speech:
discord voice: realtime exact speech queued ... queued=... outputAudioMs=... outputActive=..., followed bydiscord voice: realtime exact speech dequeued reason=player-idle ... - On barge-in detection:
discord voice: realtime barge-in detected source=speaker-start ...ordiscord voice: realtime barge-in detected source=active-speaker-audio ..., followed bydiscord voice: realtime barge-in requested reason=... outputAudioMs=... outputActive=... - On realtime interruption:
discord voice: realtime model interrupt requested client:response.cancel reason=barge-in, followed by eitherdiscord voice: realtime model audio truncated client:conversation.item.truncate reason=barge-in audioEndMs=...ordiscord voice: realtime model interrupt confirmed server:response.done status=cancelled ... - On ignored echo/noise:
discord voice: realtime model interrupt ignored client:conversation.item.truncate.skipped reason=barge-in audioEndMs=0 minAudioEndMs=250 - On disabled barge-in:
discord voice: realtime capture ignored during playback (barge-in disabled) ... - On idle playback:
discord voice: realtime barge-in ignored reason=... outputActive=false ... playbackChunks=0
realtime audio playback startedmeans Discord has begun playing assistant audio. The bridge starts counting assistant output chunks, Discord PCM bytes, provider realtime bytes, and synthesized audio duration from this point.realtime speaker turn openedmarks a Discord speaker becoming active. If playback is already active andbargeInis enabled, this can be followed bybarge-in detected source=speaker-start.realtime input audio startedmarks the first actual audio frame received for that speaker turn.outputActive=trueor a nonzerooutputAudioMshere means the mic is sending input while assistant playback is still active.barge-in detected source=active-speaker-audiomeans OpenClaw saw live speaker audio while assistant playback was active. This is useful for distinguishing a real interruption from a Discord speaker-start event with no useful audio.barge-in requested reason=...means OpenClaw asked the realtime provider to cancel or truncate the active response. It includesoutputAudioMs,outputActive, andplaybackChunksso you can see how much assistant audio had actually played before the interruption.realtime audio playback stopped reason=...is the local Discord playback reset point. The reason says who stopped playback:barge-in,player-idle,provider-clear-audio,forced-agent-consult,stream-close, orsession-close.realtime speaker turn closedsummarizes the captured input turn.chunks=0orhasAudio=falsemeans the speaker turn opened but no usable audio reached the realtime bridge.interruptedPlayback=truemeans that input turn overlapped assistant output and triggered barge-in logic.
outputAudioMs: assistant audio duration generated by the realtime provider before the log line.audioMs: assistant audio duration that OpenClaw counted before playback stopped.elapsedMs: wall-clock time between opening and closing the playback stream or speaker turn.discordBytes: 48 kHz stereo PCM bytes sent to or received from Discord voice.realtimeBytes: provider-format PCM bytes sent to or received from the realtime provider.playbackChunks: assistant audio chunks forwarded to Discord for the active response.sinceLastAudioMs: gap between the last captured speaker audio frame and the speaker turn closing.
- Immediate cut-off with
source=active-speaker-audio, smalloutputAudioMs, and the same user nearby usually points to speaker echo entering the mic. Raisevoice.realtime.minBargeInAudioEndMs, lower speaker volume, use headphones, or setvoice.realtime.providers.openai.interruptResponseOnInputAudio: false. source=speaker-startfollowed byspeaker turn closed ... hasAudio=falsemeans Discord reported a speaker start but no audio reached OpenClaw. That can be a transient Discord voice event, noise gate behavior, or a client briefly keying the mic.audio playback stopped reason=stream-closewithout a nearby barge-in orprovider-clear-audiomeans the local Discord playback stream ended unexpectedly. Check the preceding provider and Discord player logs.capture ignored during playback (barge-in disabled)means OpenClaw intentionally dropped input while assistant audio was active. Enablevoice.realtime.bargeInif you want speech to interrupt playback.barge-in ignored ... outputActive=falsemeans Discord or provider VAD reported speech, but OpenClaw had no active playback to interrupt. This should not cut off audio.
voice.model, STT auth for tools.media.audio, TTS auth for messages.tts/voice.tts, and realtime provider auth for voice.realtime.providers or the provider’s normal auth config.
Voice messages
Discord voice messages show a waveform preview and require OGG/Opus audio. OpenClaw generates the waveform automatically, but needsffmpeg and ffprobe on the gateway host to inspect and convert.
- Provide a local file path (URLs are rejected).
- Omit text content (Discord rejects text + voice message in the same payload).
- Any audio format is accepted; OpenClaw converts to OGG/Opus as needed.
Troubleshooting
Used disallowed intents or bot sees no guild messages
Used disallowed intents or bot sees no guild messages
- enable Message Content Intent
- enable Server Members Intent when you depend on user/member resolution
- restart gateway after changing intents
Guild messages blocked unexpectedly
Guild messages blocked unexpectedly
- verify
groupPolicy - verify guild allowlist under
channels.discord.guilds - if guild
channelsmap exists, only listed channels are allowed - verify
requireMentionbehavior and mention patterns
Require mention false but still blocked
Require mention false but still blocked
groupPolicy="allowlist"without matching guild/channel allowlistrequireMentionconfigured in the wrong place (must be underchannels.discord.guildsor channel entry)- sender blocked by guild/channel
usersallowlist
Long-running Discord turns or duplicate replies
Long-running Discord turns or duplicate replies
Slow listener detected ...stuck session: sessionKey=agent:...:discord:... state=processing ...
- single-account:
channels.discord.eventQueue.listenerTimeout - multi-account:
channels.discord.accounts.<accountId>.eventQueue.listenerTimeout - this only controls Discord gateway listener work, not agent turn lifetime
Gateway metadata lookup timeout warnings
Gateway metadata lookup timeout warnings
/gateway/bot metadata before connecting. Transient failures fall back to Discord’s default gateway URL and are rate-limited in logs.Metadata timeout knobs:- single-account:
channels.discord.gatewayInfoTimeoutMs - multi-account:
channels.discord.accounts.<accountId>.gatewayInfoTimeoutMs - env fallback when config is unset:
OPENCLAW_DISCORD_GATEWAY_INFO_TIMEOUT_MS - default:
30000(30 seconds), max:120000
Gateway READY timeout restarts
Gateway READY timeout restarts
READY event during startup and after runtime reconnects. Multi-account setups with startup staggering can need a longer startup READY window than the default.READY timeout knobs:- startup single-account:
channels.discord.gatewayReadyTimeoutMs - startup multi-account:
channels.discord.accounts.<accountId>.gatewayReadyTimeoutMs - startup env fallback when config is unset:
OPENCLAW_DISCORD_READY_TIMEOUT_MS - startup default:
15000(15 seconds), max:120000 - runtime single-account:
channels.discord.gatewayRuntimeReadyTimeoutMs - runtime multi-account:
channels.discord.accounts.<accountId>.gatewayRuntimeReadyTimeoutMs - runtime env fallback when config is unset:
OPENCLAW_DISCORD_RUNTIME_READY_TIMEOUT_MS - runtime default:
30000(30 seconds), max:120000
Permissions audit mismatches
Permissions audit mismatches
channels status --probe permission checks only work for numeric channel IDs.If you use slug keys, runtime matching can still work, but probe cannot fully verify permissions.DM and pairing issues
DM and pairing issues
- DM disabled:
channels.discord.dm.enabled=false - DM policy disabled:
channels.discord.dmPolicy="disabled"(legacy:channels.discord.dm.policy) - awaiting pairing approval in
pairingmode
Bot to bot loops
Bot to bot loops
channels.discord.allowBots=true, use strict mention and allowlist rules to avoid loop behavior.
Prefer channels.discord.allowBots="mentions" to only accept bot messages that mention the bot.Voice STT drops with DecryptionFailed(...)
Voice STT drops with DecryptionFailed(...)
- keep OpenClaw current (
openclaw update) so the Discord voice receive recovery logic is present - confirm
channels.discord.voice.daveEncryption=true(default) - start from
channels.discord.voice.decryptionFailureTolerance=24(upstream default) and tune only if needed - watch logs for:
discord voice: DAVE decrypt failures detecteddiscord voice: repeated decrypt failures; attempting rejoin
- if failures continue after automatic rejoin, collect logs and compare against the upstream DAVE receive history in discord.js #11419 and discord.js #11449
Configuration reference
Primary reference: Configuration reference - Discord.High-signal Discord fields
High-signal Discord fields
- startup/auth:
enabled,token,accounts.*,allowBots - policy:
groupPolicy,dm.*,guilds.*,guilds.*.channels.* - command:
commands.native,commands.useAccessGroups,configWrites,slashCommand.* - event queue:
eventQueue.listenerTimeout(listener budget),eventQueue.maxQueueSize,eventQueue.maxConcurrency - gateway:
gatewayInfoTimeoutMs,gatewayReadyTimeoutMs,gatewayRuntimeReadyTimeoutMs - reply/history:
replyToMode,historyLimit,dmHistoryLimit,dms.*.historyLimit - delivery:
textChunkLimit,chunkMode,maxLinesPerMessage - streaming:
streaming(legacy alias:streamMode),streaming.preview.toolProgress,draftChunk,blockStreaming,blockStreamingCoalesce - media/retry:
mediaMaxMb(caps outbound Discord uploads, default100MB),retry - actions:
actions.* - presence:
activity,status,activityType,activityUrl - UI:
ui.components.accentColor - features:
threadBindings, top-levelbindings[](type: "acp"),pluralkit,execApprovals,intents,agentComponents,heartbeat,responsePrefix
Safety and operations
- Treat bot tokens as secrets (
DISCORD_BOT_TOKENpreferred in supervised environments). - Grant least-privilege Discord permissions.
- If command deploy/state is stale, restart gateway and re-check with
openclaw channels status --probe.