Issue Description
Apple Mail iOS renders empty inbox despite IMAP SELECT INBOX returning * N EXISTS; Outlook desktop works on same account
Summary
Apple Mail on iOS (and macOS Mail) connects, authenticates, and renders the inbox as empty (“nothing left to read, enjoy your empty inbox”) for accounts on Stalwart v0.16.4. The server confirms ~90 messages in INBOX via raw IMAP SELECT; UID FETCH returns real messages with valid headers. Outlook desktop on the same account works correctly.
Reproduced across:
- Two end users on different home networks, two distinct Apple-Mail-on-iPhone setups
- Both iPhone-only and iPhone + macOS (macOS Mail also affected for one user)
- Two different accounts on the same server, after the standard Mozilla-style autoconfig setup flow
Outlook desktop, Thunderbird, and direct IMAP CLI all see the 90 messages correctly.
Environment
- Server: Stalwart v0.16.4, Ubuntu 24.04 aarch64
- Listeners:
imapson[::]:993(TLS implicit), Let’s Encrypt cert chain - Client: Apple Mail iOS (iOS 18.x), Apple Mail macOS (current)
- Setup path: Mozilla-style autoconfig served at
/mail/config-v1.1.xml - Capability advertisement (post-AUTH):
IMAP4rev2 IMAP4rev1 ENABLE SASL-IR LITERAL+ ID UTF8=ACCEPT JMAPACCESS IDLE NAMESPACE CHILDREN MULTIAPPEND BINARY UNSELECT ACL UIDPLUS ESEARCH WITHIN SEARCHRES SORT THREAD=REFERENCES LIST-EXTENDED LIST-STATUS ESORT SORT=DISPLAY SPECIAL-USE CREATE-SPECIAL-USE MOVE CONDSTORE QRESYNC UNAUTHENTICATE STATUS=SIZE OBJECTID PREVIEW RIGHTS=texk QUOTA QUOTA=RES-STORAGE
Server returns the data correctly
Direct IMAP from localhost via openssl s_client -connect 127.0.0.1:993:
* OK [CAPABILITY ...] Stalwart IMAP4rev2 at your service.
a01 LOGIN [email protected] <password>
a01 OK Authentication successful
a02 LIST "" "*"
* LIST (\Trash) "/" "Deleted Items"
* LIST (\Drafts) "/" "Drafts"
* LIST () "/" "INBOX"
* LIST (\Junk) "/" "Junk Mail"
* LIST (\Sent) "/" "Sent Items"
a02 OK LIST completed
a03 SELECT INBOX
* 90 EXISTS
* 0 RECENT
* OK [UIDVALIDITY 4078036059] UIDs valid
* OK [UIDNEXT 103] Next predicted UID
* OK [MAILBOXID (qaaaaaa)] Unique Mailbox ID
a03 OK [READ-WRITE] SELECT completed
a04 UID FETCH 1:3 (UID FLAGS BODY[HEADER.FIELDS (SUBJECT FROM DATE)])
* 1 FETCH (UID 1 FLAGS () INTERNALDATE "04-May-2026 16:32:14 +0000" ...)
* 2 FETCH (UID 2 FLAGS () INTERNALDATE "04-May-2026 18:15:32 +0000" ...)
* 3 FETCH (UID 3 FLAGS (\Seen) INTERNALDATE "04-May-2026 18:30:59 +0000" ...)
a04 OK FETCH completed
What we ruled out
- Folder topology /
INBOX/Notessubfolder confusing Apple’s name resolver. Flattened the tree, movedINBOX/Notesto root, deleted a duplicateSent Messagesfolder. iOS Mail behavior unchanged. - Missing IMAP extensions per Apple’s TN3191. We advertise all of the extensions called out as required/recommended there: SASL-IR, CONDSTORE, ESEARCH, ID, IDLE, LIST-STATUS, LITERAL+, MOVE, SPECIAL-USE, UIDPLUS. Missing only optional optimizations (UIDONLY/RFC 9586, APPENDLIMIT, COMPRESS, PARTIAL) — none should produce empty rendering per TN3191.
\HasChildren/\HasNoChildrenon INBOX. After folder flattening, INBOX has no children;crates/imap/src/op/list.rs:243-250correctly emits\HasNoChildrenwhen LIST-EXTENDED with CHILDREN return option is requested.- The IDLE-pipelining bug from #765. Commit
51c8f7b2is in our tree (post-v0.10.0). And per your comment on #747, “Apple clients do not use IDLE” — confirmed by the connection pattern (one fresh connection per poll, ~every 2 minutes). - fail2ban / firewall.
auth.successevents fire ~30 times/hour from the iPhone’s IP for the affected account. - Credentials. Same creds work in Outlook desktop, Thunderbird, and direct IMAP CLI on the same iOS device’s network.
What we observed (and where we got stuck)
After each auth.success event from the iPhone’s IP, the server log shows no subsequent IMAP-related events on that connection — no imap.select, imap.fetch, imap.list, imap.logout. Either:
- (a) iOS Mail authenticates and disconnects without issuing commands (would suggest client-side bail after auth), or
- (b) the IMAP-command events fire but aren’t captured in our release-build log even at
Tracer.level = "trace". Specifically, thetrc::event!(Imap(trc::ImapEvent::Fetch), ...)calls atcrates/imap/src/op/fetch.rs:237,op/select.rs:109,op/list.rs:296, etc. don’t produce log entries at trace level on a release build — the same is true for my local self-test from inside the box, which performs a fullLOGIN -> SELECT INBOX -> UID FETCH -> LOGOUTand produces only oneauth.successINFO entry, no Select/Fetch/Logout entries.
If (b), there may be a separate logging-level issue worth fixing — trc::event! for Imap subcommands appears to be filtered out of release builds, which blocks server-side wire-level debugging without rebuild.
Why filing here
The IMAP server is RFC-conformant — every non-Apple client works. The bug is specific to Apple Mail iOS/macOS, and given your IETF 123 contact with the Apple Mail team (per #747) you may already have visibility into a known iOS Mail / IMAP server behavior that produces this symptom against Stalwart specifically. The general shape (“Apple Mail decides INBOX is empty while a parallel client correctly sees N messages”) matches old reports against Dovecot and Cyrus (e.g., Apple Discussions thread 7904861 re: NAMESPACE mismatches in iOS 10.3) but those don’t have a published server-side fix recipe.
Asks
- Has anyone reported “Apple Mail iOS shows empty inbox while SELECT INBOX returns valid
* N EXISTS” against current Stalwart v0.16.x? - Is the absence of
imap.select/imap.fetch/imap.listevents in trace-level logs intentional (release-build filtering), or a regression worth fixing separately? - Open to a reproducer PR (tests crate, two-connection setup with Apple-Mail-style command sequence) if that would help triage?
Happy to capture an mitmproxy TLS-decrypted wire trace from a live iPhone connection if you can suggest the specific commands to look for. We can also share TCP-level packet timing if useful.
Expected Behavior
Apple Mail on iOS (and macOS Mail), after a standard account add via Mozilla-style autoconfig against a Stalwart IMAP server, should display the inbox contents — the same messages that SELECT INBOX reports via * N EXISTS and that UID FETCH returns from the server.
Apple’s TN3191 — IMAP extensions supported by Mail lists the extensions Mail requires/recommends; Stalwart advertises all of them. Per RFC 3501, a folder named INBOX is the canonical inbox and should be displayed by any compliant IMAP client.
Actual Behavior
Apple Mail on iOS connects, authenticates successfully, and then renders the inbox view as empty — the iOS placeholder text “nothing left to read, enjoy your empty inbox” is shown. macOS Mail behaves identically for the same user/account on the same network.
Server-side, SELECT INBOX returns * 91 EXISTS for the same account at the same moment, and UID FETCH 1:3 (UID FLAGS BODY[HEADER.FIELDS (SUBJECT FROM DATE)]) returns real messages with valid headers (verified via raw openssl s_client -connect 127.0.0.1:993). Outlook desktop, Thunderbird, and direct IMAP CLI all see the messages correctly when pointed at the same account.
Deviation: the inbox is not empty (91 messages exist); Apple Mail iOS and macOS Mail uniquely fail to display them. Outlook desktop on the same account works correctly, so the protocol/server side cannot be entirely broken.
The bug reproduces across:
- Two end users on different home networks, two distinct Apple-Mail-on-iPhone setups
- Both iPhone-only and iPhone + macOS (macOS Mail also affected for one user)
- Two different accounts on the same server, after the standard autoconfig setup flow
Reproduction Steps
- Install Stalwart v0.16.4 on Ubuntu 24.04 (aarch64) with the IMAP listener bound to
[::]:993withtlsImplicit = trueand a publicly-trusted (Let’s Encrypt) cert. - Create a user account (e.g.
[email protected]) and populate the inbox with ~90 messages via SMTP delivery. - On an iPhone running iOS 18.x, open Settings → Mail → Add Account → Other → Add Mail Account, enter the account credentials, accept the autoconfig that resolves via the Mozilla-style
/mail/config-v1.1.xmlendpoint (IMAP host port 993 SSL, SMTP host port 465 SSL, full email as username, password-cleartext). - Wait 1–2 minutes for the initial sync.
- Open Mail.app, navigate to the account’s Inbox view.
- Observe: empty inbox placeholder rather than ~90 messages.
- From inside the server, run
openssl s_client -connect 127.0.0.1:993 -servername <host>withLOGIN [email protected] <pw>thenSELECT INBOX— observe* 91 EXISTS(the messages exist; Apple Mail’s empty render is not consistent with server state). - Set up the same account in Outlook desktop or Thunderbird on a separate machine — observe all 91 messages display correctly. We also tried (all without effect on the iOS empty-inbox symptom):
- Flattened the mailbox tree so INBOX has no children. Deleted a duplicate
Sent Messagesfolder leaving only the\Sent-flaggedSent Items. - Verified our advertised IMAP capabilities cover every required/recommended extension in TN3191 (SASL-IR, CONDSTORE, ESEARCH, ID, IDLE, LIST-STATUS, LITERAL+, MOVE, SPECIAL-USE, UIDPLUS are all advertised; only optional optimisations UIDONLY/RFC 9586, APPENDLIMIT, COMPRESS, PARTIAL are missing).
- Verified
\HasChildren/\HasNoChildrenare emitted correctly on LIST-EXTENDED with CHILDREN return option (crates/imap/src/op/list.rs:243-250). - Confirmed the IDLE-pipelining fix (commit
51c8f7b2, shipped in v0.10.0) is present. - Confirmed credentials are correct (same creds work in Outlook desktop, Thunderbird, and direct IMAP CLI on the same iOS device’s network).
- Confirmed no
fail2banblock — auth succeeds ~30 times/hour from the iPhone’s IP without error. - Deleted the account from the iPhone, deleted saved entries from Settings → Passwords, full power-cycle, re-added fresh via autoconfig. Same empty result.
- Cherry-picked upstream commit
77042480ae(“Fix IMAP: UID FETCH N:* could miss messages moved into a SELECTed mailbox by another connection”) on the speculation that Apple Mail’s per-folder parallel connections + aggressive MOVEs could be hitting this race. Rebuilt and deployed; iPhone re-tested against the patched binary; symptom persists. Falsifies that specific hypothesis.
Relevant Log Output
Server runs with Tracer.level = "info" by default. Per-connection IMAP auth events appear cleanly:
2026-05-14T18:49:04Z INFO Authentication successful (auth.success) listenerId = "imaps", localPort = 993, remoteIp = <iPhone-public-IP>, remotePort = 49319, accountName = "[email protected]", accountId = 4
2026-05-14T18:49:04Z INFO Authentication successful (auth.success) listenerId = "imaps", localPort = 993, remoteIp = <iPhone-public-IP>, remotePort = 49320, accountName = "<other-user>@example.com", accountId = 3
2026-05-14T18:49:15Z INFO SMTP EHLO command (smtp.ehlo) listenerId = "submissions", localPort = 465, remoteIp = <iPhone-public-IP>, remotePort = 49321, domain = "localhost"
After each auth.success from the iPhone’s IP, no subsequent IMAP-related events appear on that connection — no imap.select, imap.fetch, imap.list, imap.logout. The two interpretations are:
- (a) iOS Mail authenticates and disconnects without issuing commands.
- (b) The IMAP-command events fire in source but aren’t captured in our release-build log even at
Tracer.level = "trace".
To test (b): I bumped the tracer level to trace, then ran a full local LOGIN → SELECT INBOX → UID FETCH 1:3 → LOGOUT sequence via openssl s_client from inside the box. Only the single auth.success INFO entry was produced — no imap.select, imap.fetch, or imap.logout log entries despite the trace-level setting. The relevant trc::event!(Imap(trc::ImapEvent::Fetch), ...) calls at crates/imap/src/op/fetch.rs:237, op/select.rs:109, op/list.rs:296, and op/logout.rs did not produce log output at trace level on a release build. If this is intentional (release-only event filtering), it blocks server-side wire-level debugging of Apple Mail issues without a debug rebuild.
Raw IMAP exchange from openssl s_client (server side is healthy):
* OK [CAPABILITY IMAP4rev2 IMAP4rev1 ENABLE SASL-IR LITERAL+ ID UTF8=ACCEPT JMAPACCESS AUTH=PLAIN AUTH=OAUTHBEARER AUTH=XOAUTH2] Stalwart IMAP4rev2 at your service.
a01 LOGIN [email protected] <password>
a01 OK [CAPABILITY IMAP4rev2 IMAP4rev1 ENABLE SASL-IR LITERAL+ ID UTF8=ACCEPT JMAPACCESS IDLE NAMESPACE CHILDREN MULTIAPPEND BINARY UNSELECT ACL UIDPLUS ESEARCH WITHIN SEARCHRES SORT THREAD=REFERENCES LIST-EXTENDED LIST-STATUS ESORT SORT=DISPLAY SPECIAL-USE CREATE-SPECIAL-USE MOVE CONDSTORE QRESYNC UNAUTHENTICATE STATUS=SIZE OBJECTID PREVIEW RIGHTS=texk QUOTA QUOTA=RES-STORAGE] Authentication successful
a02 LIST "" "*"
* LIST (\Trash) "/" "Deleted Items"
* LIST (\Drafts) "/" "Drafts"
* LIST () "/" "INBOX"
* LIST (\Junk) "/" "Junk Mail"
* LIST (\Sent) "/" "Sent Items"
a02 OK LIST completed
a03 SELECT INBOX
* 91 EXISTS
* 0 RECENT
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
* OK [PERMANENTFLAGS (\Deleted \Seen \Answered \Flagged \Draft \*)] All allowed
* OK [UIDVALIDITY 4078036059] UIDs valid
* OK [UIDNEXT 104] Next predicted UID
* OK [MAILBOXID (qaaaaaa)] Unique Mailbox ID
a03 OK [READ-WRITE] SELECT completed
a04 UID FETCH 1:3 (UID FLAGS BODY[HEADER.FIELDS (SUBJECT FROM DATE)])
* 1 FETCH (UID 1 FLAGS () INTERNALDATE "04-May-2026 16:32:14 +0000" ...)
* 2 FETCH (UID 2 FLAGS () INTERNALDATE "04-May-2026 18:15:32 +0000" ...)
* 3 FETCH (UID 3 FLAGS (\Seen) INTERNALDATE "04-May-2026 18:30:59 +0000" ...)
a04 OK FETCH completed
a05 LOGOUT
* BYE Stalwart IMAP4rev2 bids you farewell.
a05 OK LOGOUT completed
Stalwart Version
v0.16.x
Installation Method
Built from source
Database Backend
RocksDB
Blob Storage
Filesystem
Search Engine
Internal
Directory Backend
Internal
I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.
on
I have searched this support forum (open and closed topics) and confirm this is not a duplicate.
on
I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.
on