From a21243256e9e99a7e5e1d7f2e0fc7391646eb604 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Thu, 26 Mar 2026 14:27:48 +0100 Subject: [PATCH] fix(federation): fix reaction relay using wrong ID for message lookup Reaction outbox events used reactionId (add) or a composite dedup key (remove) as the event messageId. The receiver looked up dm_messages by sourceMessageId = event.messageId, which never matched because it was searching for a reactionId, not the actual message ID. Fix: include the actual DM messageId in the reaction payload JSON. The receiver now uses event.reaction.messageId for the lookup, with fallback to event.messageId for backward compatibility. --- packages/server/src/routes/federation.ts | 12 ++++++++---- packages/server/src/ws/events.ts | 2 ++ packages/shared/src/types.ts | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/server/src/routes/federation.ts b/packages/server/src/routes/federation.ts index 36e44495..118741ca 100644 --- a/packages/server/src/routes/federation.ts +++ b/packages/server/src/routes/federation.ts @@ -1252,14 +1252,16 @@ function processReactionAddEvent( return; } - // Find the local message corresponding to the source message + // Find the local message — use the actual message ID from the reaction payload, + // not event.messageId which is the outbox dedup key (reactionId) + const sourceMessageId = event.reaction.messageId ?? event.messageId; const localMsg = db .select() .from(schema.dmMessages) .where( and( eq(schema.dmMessages.sourceInstance, sourceInstance), - eq(schema.dmMessages.sourceMessageId, event.messageId), + eq(schema.dmMessages.sourceMessageId, sourceMessageId), ), ) .get(); @@ -1337,14 +1339,16 @@ function processReactionRemoveEvent( return; } - // Find the local message + // Find the local message — use the actual message ID from the reaction payload, + // not event.messageId which is the outbox dedup key (composite string) + const sourceMessageId = event.reaction.messageId ?? event.messageId; const localMsg = db .select() .from(schema.dmMessages) .where( and( eq(schema.dmMessages.sourceInstance, sourceInstance), - eq(schema.dmMessages.sourceMessageId, event.messageId), + eq(schema.dmMessages.sourceMessageId, sourceMessageId), ), ) .get(); diff --git a/packages/server/src/ws/events.ts b/packages/server/src/ws/events.ts index 0203d052..ed39ccfb 100644 --- a/packages/server/src/ws/events.ts +++ b/packages/server/src/ws/events.ts @@ -1135,6 +1135,7 @@ function handleReactionAdd(event: Record, userId: string): void })); queueOutboxEvent(reactionId, dmMsg.dmChannelId, 'reaction_add', JSON.stringify({ reaction: { + messageId, userId, homeUserId: reactionUser?.homeUserId || userId, emoji, @@ -1212,6 +1213,7 @@ function handleReactionRemove(event: Record, userId: string): v 'reaction_remove', JSON.stringify({ reaction: { + messageId, userId, homeUserId: removingUser?.homeUserId || userId, emoji, diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index 95ca25a5..3a832594 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -744,6 +744,7 @@ export interface FederationRelayEvent { } export interface FederationRelayReaction { + messageId?: string; userId: string; homeUserId: string; emoji: string;