Discord plugin: DM replies fail with 'channel not allowlisted' due to null recipientId
Bug
The Discord plugin's outbound gate (fetchAllowedChannel) fails to reply to DM channels even when the user is correctly allowlisted in access.json.
Root cause
In server.ts, the inbound gate (gate()) correctly uses msg.author.id to check the allowlist (line 241/245):
const senderId = msg.author.id
// ...
if (access.allowFrom.includes(senderId)) return { action: 'deliver', access }
But the outbound gate (fetchAllowedChannel) relies on ch.recipientId from a fetched DMChannel (line 406-407):
if (ch.type === ChannelType.DM) {
if (access.allowFrom.includes(ch.recipientId)) return ch
}
DMChannel.recipientId can be null when the channel is fetched via client.channels.fetch() with a cold cache (e.g., after plugin/session restart). This causes allowFrom.includes(null) → false → throws "channel not allowlisted".
Reproduction
- Configure
access.jsonwith a user ID inallowFromanddmPolicy: "allowlist" - Restart the Claude Code session (cold Discord.js cache)
- Receive a DM from the allowlisted user (works — inbound uses
msg.author.id) - Try to reply via the
replytool → fails with "channel {id} is not allowlisted"
The bug is intermittent: it works when the DM channel is still in Discord.js's cache (e.g., within the same session after receiving a message), but breaks after restarts.
Suggested fix
Maintain a Map<channelId, userId> populated during inbound message handling, and use it as a fallback when recipientId is null:
// After recentSentIds
const dmChannelUsers = new Map<string, string>()
// In handleInbound, after gate() succeeds:
if (msg.channel.type === ChannelType.DM) {
dmChannelUsers.set(msg.channelId, msg.author.id)
}
// In fetchAllowedChannel:
if (ch.type === ChannelType.DM) {
const recipientId = ch.recipientId ?? dmChannelUsers.get(id)
if (recipientId && access.allowFrom.includes(recipientId)) return ch
}
This is a minimal 3-line change that resolves the issue without changing the security model — the map is only populated for users who already passed the inbound gate.
Environment
- Claude Code CLI
- Discord plugin v0.0.4
- macOS
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗