Steps to reproduce
- Compose a new message.
- Attach one or more local files (drag & drop / upload). The upload succeeds —
POST /apps/mail/api/attachments → 201, blob is stored, an oc_mail_attachments row is created.
- Do not send. Let the composer auto-save and/or navigate away, so the draft is pushed to the IMAP Drafts mailbox:
POST /apps/mail/api/drafts → 201
POST /apps/mail/api/drafts/move/{id} → 202
- Open the Drafts folder and reopen that draft.
Expected behavior
The draft reopens with the attachment(s) still attached, ready to send.
Actual behavior
- Compose a new message.
- Attach one or more local files (drag & drop / upload). The upload succeeds —
POST /apps/mail/api/attachments → 201, blob is stored, an oc_mail_attachments row is created.
- Do not send. Let the composer auto-save and/or navigate away, so the draft is pushed to the IMAP Drafts mailbox:
POST /apps/mail/api/drafts → 201
POST /apps/mail/api/drafts/move/{id} → 202
- Open the Drafts folder and reopen that draft.
Expected behaviour
The draft reopens with the attachment(s) still attached, ready to send.
Actual behaviour
- The attachment is gone from the reopened draft.
- The locally-uploaded attachment row is left orphaned (
oc_mail_attachments.local_message_id = NULL) and its appdata blob is stranded.
- The corresponding message cached for the Drafts mailbox has
flag_attachments = 0 — i.e. the attachment was not carried into the IMAP draft.
- Continuing to edit/re-save the reopened draft throws on the server:
InvalidArgumentException: Attachment does not have a type
at OCA\Mail\Service\Attachment\AttachmentService->handleAttachments() lib/Service/Attachment/AttachmentService.php:253
from OCA\Mail\Service\DraftsService->handleAttachments() lib/Service/DraftsService.php:118
via OCA\Mail\Controller\DraftsController->saveMessage() lib/Controller/DraftsController.php:127
POST /apps/mail/api/drafts
The check at AttachmentService.php:253 is:
foreach ($attachments as $attachment) {
if (!isset($attachment['type'])) {
throw new InvalidArgumentException('Attachment does not have a type');
}
...
so the frontend is submitting at least one attachment object without a type property when continuing a draft that was round-tripped through the IMAP Drafts folder.
Works as a contrast (no IMAP Drafts round-trip)
Composing → attaching → sending in one sitting works correctly and the recipient gets the attachment. The flow that succeeds goes straight to the outbox without a drafts/move:
POST /apps/mail/api/attachments 201 (xN)
POST /apps/mail/api/drafts 201
POST /apps/mail/api/outbox/from-draft/{id} 201
POST /apps/mail/api/outbox/{id} 202 (sent, with attachments)
The problem only manifests when the draft is saved to / reopened from the IMAP Drafts mailbox.
Regression window
The Attachment does not have a type error first appears in our logs immediately after upgrading into the 5.10.x line (first seen the days following the 5.10.0 release) and never occurred on 5.9.x. This looks like a regression introduced in 5.10.0.
Side effect
Because the linking step throws, every failed attempt leaves an orphaned oc_mail_attachments row (local_message_id = NULL) plus its appdata blob. These accumulate over time (we found 91 stranded blobs) and are never garbage-collected.
Mail app version
5.10.3
Nextcloud version
34.0.0
Mailserver or service
IMAP/SMTP: Dovecot/Exim, app-password auth (auth verified healthy — sending and IMAP login both work)
Operating system
Debian (Linux 6.12) / Docker (nextcloud:34-apache)
PHP engine version
Other
Nextcloud memory caching
'memcache.local' => '\OC\Memcache\APCu',
Web server
Apache (supported)
Database
MariaDB
Additional info
PHP 8.4.22
MariaDB 11.8
Steps to reproduce
POST /apps/mail/api/attachments→201, blob is stored, anoc_mail_attachmentsrow is created.POST /apps/mail/api/drafts→201POST /apps/mail/api/drafts/move/{id}→202Expected behavior
The draft reopens with the attachment(s) still attached, ready to send.
Actual behavior
POST /apps/mail/api/attachments→201, blob is stored, anoc_mail_attachmentsrow is created.POST /apps/mail/api/drafts→201POST /apps/mail/api/drafts/move/{id}→202Expected behaviour
The draft reopens with the attachment(s) still attached, ready to send.
Actual behaviour
oc_mail_attachments.local_message_id = NULL) and its appdata blob is stranded.flag_attachments = 0— i.e. the attachment was not carried into the IMAP draft.The check at
AttachmentService.php:253is:so the frontend is submitting at least one attachment object without a
typeproperty when continuing a draft that was round-tripped through the IMAP Drafts folder.Works as a contrast (no IMAP Drafts round-trip)
Composing → attaching → sending in one sitting works correctly and the recipient gets the attachment. The flow that succeeds goes straight to the outbox without a
drafts/move:The problem only manifests when the draft is saved to / reopened from the IMAP Drafts mailbox.
Regression window
The
Attachment does not have a typeerror first appears in our logs immediately after upgrading into the 5.10.x line (first seen the days following the 5.10.0 release) and never occurred on 5.9.x. This looks like a regression introduced in 5.10.0.Side effect
Because the linking step throws, every failed attempt leaves an orphaned
oc_mail_attachmentsrow (local_message_id = NULL) plus its appdata blob. These accumulate over time (we found 91 stranded blobs) and are never garbage-collected.Mail app version
5.10.3
Nextcloud version
34.0.0
Mailserver or service
IMAP/SMTP: Dovecot/Exim, app-password auth (auth verified healthy — sending and IMAP login both work)
Operating system
Debian (Linux 6.12) / Docker (nextcloud:34-apache)
PHP engine version
Other
Nextcloud memory caching
'memcache.local' => '\OC\Memcache\APCu',
Web server
Apache (supported)
Database
MariaDB
Additional info
PHP 8.4.22
MariaDB 11.8