Skip to content

Some emails are blank - Not Found ! (Mailfence) #12584

@Mat-London

Description

@Mat-London

Steps to reproduce

  1. Connect a Mailfence IMAP account to Nextcloud Mail
  2. Open the inbox
  3. Click on certain emails — particularly newsletters, large HTML emails
  4. The message body fails to load

Expected behavior

The email body loads and displays correctly. The email exists on the server and is retrievable — View Source works for affected messages and displays the full content.

Actual behavior

The message body shows "Not Found" in the UI. The API endpoint /api/messages/{id}/body returns HTTP 500.
The following exception appears in the logs:

DoesNotExistException: Mail body for this mail({uid}) could not be loaded
at OCA\Mail\IMAP\ImapMessageFetcher->loadBodyData()
at OCA\Mail\IMAP\ImapMessageFetcher->handleHtmlMessage()
at OCA\Mail\IMAP\ImapMessageFetcher->getPart()
at OCA\Mail\IMAP\ImapMessageFetcher->fetchMessage()

The failure affects a consistent subset of messages — not random. Headers, flags, and envelope data all load correctly for the same messages. View Source retrieves the full raw message successfully, confirming the IMAP connection and message existence are not the problem.

Mail app version

5.7.3

Nextcloud version

32.0.6.1

Mailserver or service

Mailfence.com

Operating system

Ubuntu 20.04

PHP engine version

PHP 8.2

Nextcloud memory caching

memcache.local: APCu, memcache.distributed: Redis, memcache.locking: Redis

Web server

Apache (supported)

Database

MySQL

Additional info

Root cause (identified)
loadBodyData() issues a single IMAP FETCH request combining BODY[$partNo] and BODY[$partNo.MIME]. For certain messages, the IMAP server (Mailfence) returns no FETCH data at all for the UID, making $headers[$this->uid] null and triggering the exception. The BODY[$partNo.MIME] sub-request appears to be the trigger — it is not valid for single-part messages where no MIME sub-section exists.

Proposed fix
Catch DoesNotExistException in handleHtmlMessage and handleTextMessage, then fall back to fetching the full raw message via fullText and extracting the part using Horde_Mime_Part::parseMessage() — the same mechanism used by View Source, which succeeds for all affected messages.

--- a/lib/IMAP/ImapMessageFetcher.php
+++ b/lib/IMAP/ImapMessageFetcher.php
@@ handleTextMessage

  •         $data = $this->loadBodyData($p, $partNo, $isFetched);                                       
    
  •         $this->plainMessage .= trim($data) . "\n\n";                                                
    
  •         try {                                                                                       
    
  •                 $data = $this->loadBodyData($p, $partNo, $isFetched);                               
    
  •                 $this->plainMessage .= trim($data) . "\n\n";                                        
    
  •         } catch (DoesNotExistException $e) {                                                        
    
  •                 $fb = $this->loadBodyDataFallback($partNo);                                         
    
  •                 if ($fb !== null) {                                                                 
    
  •                         $this->plainMessage .= trim($fb) . "\n\n";                                  
    
  •                 }                                                                                   
    
  •         }                                                                                           
    

@@ handleHtmlMessage

  •         $this->hasHtmlMessage = true;                                                               
    
  •         $data = $this->loadBodyData($p, $partNo, $isFetched);                                       
    
  •         $this->htmlMessage .= $data . '<br><br>';                                                   
    
  •         try {                                                                                       
    
  •                 $data = $this->loadBodyData($p, $partNo, $isFetched);                               
    
  •                 $this->hasHtmlMessage = true;                                                       
    
  •                 $this->htmlMessage .= $data . '<br><br>';                                           
    
  •         } catch (DoesNotExistException $e) {                                                        
    
  •                 $fb = $this->loadBodyDataFallback($partNo);                                         
    
  •                 if ($fb !== null) {                                                                 
    
  •                         $this->hasHtmlMessage = true;                                               
    
  •                         $this->htmlMessage .= $fb . '<br><br>';                                     
    
  •                 }                                                                                   
    
  •         }                                                                                           
    
  • private function loadBodyDataFallback(string $partNo): ?string {                                    
    
  •         try {                                                                                       
    
  •                 $q = new Horde_Imap_Client_Fetch_Query();                                           
    
  •                 $q->fullText(['peek' => true]);                                                     
    
  •                 $ids = new Horde_Imap_Client_Ids($this->uid);                                       
    
  •                 $r = $this->client->fetch($this->mailbox, $q, ['ids' => $ids]);                     
    
  •                 $fetch = $r[$this->uid];                                                            
    
  •                 if (is_null($fetch)) { return null; }                                               
    
  •                 $txt = $fetch->getFullMsg();                                                        
    
  •                 if (empty($txt)) { return null; }                                                   
    
  •                 $parsed = Horde_Mime_Part::parseMessage($txt, ['forcemime' => true]);               
    
  •                 $part = $parsed[$partNo];                                                           
    
  •                 if ($part === null) { return null; }                                                
    
  •                 return $this->converter->convert($part);                                            
    
  •         } catch (\Exception $e) {                                                                   
    
  •                 return null;                                                                        
    
  •         }                                                                                           
    
  • }                                                                                                   
    

Additional info: This fix has been tested on the system described above. I originally reported this in #5485 (August 2021, closed as completed). The underlying cause was never resolved — the same error has recurred across subsequent versions. The fix proposed here was developed against v5.7.3 and confirmed working. Bugs with similar symptoms have been reported repeatedly since at least 2018 across issues #4400, #6913, #8223, #8400, and #9048. All show the identical stack trace. The common thread is that View Source always works for affected messages while body loading fails — pointing to the fetch query rather than IMAP connectivity. Related issues have been closed without fixing the underlying cause.

Metadata

Metadata

Assignees

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions