From 48aa43517886014e90ee80a6461f9de75045369d Mon Sep 17 00:00:00 2001 From: "Daisy S. Hollman" Date: Tue, 14 Apr 2026 14:46:42 -0700 Subject: [PATCH] fix(discord): use cached author.id when DMChannel.recipientId is null (#1365) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DMChannel.recipientId can be null when client.channels.fetch() returns a DM channel with a cold cache. The inbound gate correctly uses msg.author.id, but fetchAllowedChannel relied on recipientId, so replies to allowlisted DMs intermittently failed with "channel not allowlisted" after session restart. Maintain a channelId→userId map populated during inbound handling and fall back to it when recipientId is null. Fixes anthropics/claude-code#40576 Fixes anthropics/claude-code#41647 :house: Remote-Dev: homespace --- external_plugins/discord/server.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/external_plugins/discord/server.ts b/external_plugins/discord/server.ts index 1752ebf..0595fc7 100644 --- a/external_plugins/discord/server.ts +++ b/external_plugins/discord/server.ts @@ -222,6 +222,8 @@ type GateResult = const recentSentIds = new Set() const RECENT_SENT_CAP = 200 +const dmChannelUsers = new Map() + function noteSent(id: string): void { recentSentIds.add(id) if (recentSentIds.size > RECENT_SENT_CAP) { @@ -404,7 +406,8 @@ async function fetchAllowedChannel(id: string) { const ch = await fetchTextChannel(id) const access = loadAccess() if (ch.type === ChannelType.DM) { - if (access.allowFrom.includes(ch.recipientId)) return ch + const userId = ch.recipientId ?? dmChannelUsers.get(id) + if (userId && access.allowFrom.includes(userId)) return ch } else { const key = ch.isThread() ? ch.parentId ?? ch.id : ch.id if (key in access.groups) return ch @@ -823,6 +826,10 @@ async function handleInbound(msg: Message): Promise { const chat_id = msg.channelId + if (msg.channel.type === ChannelType.DM) { + dmChannelUsers.set(chat_id, msg.author.id) + } + // Permission-reply intercept: if this looks like "yes xxxxx" for a // pending permission request, emit the structured event instead of // relaying as chat. The sender is already gate()-approved at this point