This problem is still there in iOS 26.6.1 (Dovecot 2.4.4)
Client fetches metadata: C> UID FETCH 959 (UID RFC822.SIZE BODYSTRUCTURE) → RFC822.SIZE 6928200
It then fetches the container part BODY.PEEK[1] — a multipart/related of 297 588 B sitting next to two large PDFs. For a multipart section BODYSTRUCTURE carries no octet count (body-fld-octets exists only for body-type-1part), and no other IMAP command reports it, so the client cannot know the size of what it is fetching.
It plans ten 384 KiB slices from RFC822.SIZE — the size of the whole message — and sends all ten pipelined in a single write. Nine land entirely past the end of the part.
Dovecot answers all ten correctly and immediately: BODY[1]<0> {297588} truncated, then nine × {0}, each with its tagged OK Fetch completed. 417 ms total. RFC 9051 §6.4.5 mandates exactly this ("If the starting octet is beyond the end of the text, an empty string is returned").
The client has ACKed every byte (Ack=292827, window open) and then does nothing for 93 s. Other connections from the same device keep exchanging data with the same server throughout.
Its own watchdog fires — Connection appears to have been stuck for 89.3 s. Cancelling. — then close_notify + FIN. Hence the quantisation of stall durations at 88–95 s (426 of 539 stalls over two weeks).
The retry succeeds instantly: new connection, now knowing the real part size, requests only the slices that exist. 137 ms in one measured case — same message, same server, same second.
The defect is step 5: a fully conformant response leads to a stall. Nothing here is a Dovecot bug. Seems like a race within the iOS Mailing Stack...