Search result opens wrong email due to stale local UID mapping #500

Closed
opened 2026-06-06 18:05:26 +00:00 by guettlibot · 0 comments
guettlibot commented 2026-06-06 18:05:26 +00:00 (Migrated from codeberg.org)

Root Cause

searchEmails fetches results directly from the live IMAP server and constructs email IDs as "$accountId:$uid" (see email_repository_impl.dart line 3085). When the user taps a result, getEmail(emailId) looks up that same ID in the local SQLite database (line 217). If the local DB has a stale record at that UID — because the server reused the UID after a delete or move, or because UIDVALIDITY changed — the detail screen loads a completely different email than the one shown in the search list.

This matches the "Discrepancies found" warning visible in the accounts view.

Steps to Reproduce

  1. Open a mailbox where "Discrepancies found" is reported
  2. Type a search query in the folder view search bar
  3. Tap the first result
  4. Observe that the detail screen shows a different email than expected

Debugging

1. Check the discrepancy details from the DB:

sqlite3 ~/.local/share/sharedinbox/db.sqlite   "SELECT account_id, discrepancy_summary FROM account_reliability_history ORDER BY last_verified_at DESC LIMIT 5;"

2. Confirm UID staleness — replace UID with the number after the colon in the tapped email's ID:

sqlite3 ~/.local/share/sharedinbox/db.sqlite   "SELECT id, subject, mailbox_path FROM emails WHERE id LIKE '%:UID' LIMIT 5;"

If the subject in the DB row differs from the subject shown in the search list, the UID has been reused on the server and the local record is stale.

3. Add a temporary log in email_list_screen.dart to see this at runtime:

Future<void> _openSearchResultAndRefresh(String emailId) async {
  final local = await ref.read(emailRepositoryProvider).getEmail(emailId);
  debugPrint('[search] tapped id=$emailId local="${local?.subject}"');
  // compare with the subject shown in the search tile

Fix Direction

When navigating from a search result, cross-check the ID against the already-fetched _searchResults entry. If the local DB record's subject does not match the IMAP envelope subject, either re-sync the affected UID before opening, or fetch the email body directly by UID (bypassing the stale local row).

Alternatively, store the full IMAP envelope from the search response in the local DB before navigating, so getEmail always returns fresh data for search-sourced IDs.

Closes / related to #499

## Root Cause `searchEmails` fetches results directly from the live IMAP server and constructs email IDs as `"$accountId:$uid"` (see `email_repository_impl.dart` line 3085). When the user taps a result, `getEmail(emailId)` looks up that same ID in the **local SQLite database** (line 217). If the local DB has a stale record at that UID — because the server reused the UID after a delete or move, or because UIDVALIDITY changed — the detail screen loads a completely different email than the one shown in the search list. This matches the "Discrepancies found" warning visible in the accounts view. ## Steps to Reproduce 1. Open a mailbox where "Discrepancies found" is reported 2. Type a search query in the folder view search bar 3. Tap the first result 4. Observe that the detail screen shows a different email than expected ## Debugging **1. Check the discrepancy details from the DB:** ```bash sqlite3 ~/.local/share/sharedinbox/db.sqlite "SELECT account_id, discrepancy_summary FROM account_reliability_history ORDER BY last_verified_at DESC LIMIT 5;" ``` **2. Confirm UID staleness — replace `UID` with the number after the colon in the tapped email's ID:** ```bash sqlite3 ~/.local/share/sharedinbox/db.sqlite "SELECT id, subject, mailbox_path FROM emails WHERE id LIKE '%:UID' LIMIT 5;" ``` If the subject in the DB row differs from the subject shown in the search list, the UID has been reused on the server and the local record is stale. **3. Add a temporary log in `email_list_screen.dart` to see this at runtime:** ```dart Future<void> _openSearchResultAndRefresh(String emailId) async { final local = await ref.read(emailRepositoryProvider).getEmail(emailId); debugPrint('[search] tapped id=$emailId local="${local?.subject}"'); // compare with the subject shown in the search tile ``` ## Fix Direction When navigating from a search result, cross-check the ID against the already-fetched `_searchResults` entry. If the local DB record's subject does not match the IMAP envelope subject, either re-sync the affected UID before opening, or fetch the email body directly by UID (bypassing the stale local row). Alternatively, store the full IMAP envelope from the search response in the local DB before navigating, so `getEmail` always returns fresh data for search-sourced IDs. Closes / related to #499
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: guettli/sharedinbox#500