From 4acebe55e5d83bffb911b06ef1f92b248eab312a Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Mon, 20 Jul 2026 14:03:19 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20stop=20cross-folder=20message=5Fid=20ded?= =?UTF-8?q?up=20=E2=80=94=20mails=20were=20being=20moved=20between=20folde?= =?UTF-8?q?rs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - message_id dedup was searching across ALL folders - A mail in Trash with same message_id as one in INBOX was found and moved to INBOX - Now message_id dedup only searches within the SAME folder - This matches how real mail clients work: same message_id in different folders = separate copies --- app/plugins/builtins/mail/services.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/plugins/builtins/mail/services.py b/app/plugins/builtins/mail/services.py index 517ec61..8bd9c01 100644 --- a/app/plugins/builtins/mail/services.py +++ b/app/plugins/builtins/mail/services.py @@ -636,7 +636,9 @@ async def imap_sync_folder( ) ).scalar_one_or_none() - # Also check by message_id — mail may have been moved to Trash by IMAP server + # Also check by message_id — but ONLY within the same folder + # to prevent cross-folder moves. A mail in Trash and INBOX with + # the same message_id are separate copies (like real mail clients). if not existing_mail: # Fetch headers first to get Message-ID fetch_hdr = await client.uid('fetch', uid_str, '(BODY.PEEK[HEADER.FIELDS (MESSAGE-ID)])') @@ -655,6 +657,7 @@ async def imap_sync_folder( and_( Mail.account_id == account.id, Mail.message_id == peek_msg_id, + Mail.folder_id == folder.id, Mail.tenant_id == tenant_id, ) ) @@ -1163,6 +1166,7 @@ async def imap_sync_account( and_( Mail.account_id == account.id, Mail.message_id == message_id, + Mail.folder_id == folder.id, Mail.tenant_id == tenant_id, ) )