Compare commits

...
Author SHA1 Message Date
Thomas SharedInboxandClaude Sonnet 4.6 3ef4ec3094 fix: guard against empty IMAP fetch message list (#339)
Replace unsafe .first on fetch.messages with .firstOrNull + early throw in
getEmailBody, downloadAttachment, and fetchRawRfc822, so a UID-expunged or
network-hiccup response gives a descriptive StateError instead of the
opaque "No element" crash. Also add snapshot.hasError handling in the
FutureBuilder so users see an error message rather than an infinite spinner.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 18:05:25 +02:00
2 changed files with 30 additions and 3 deletions
@@ -237,7 +237,12 @@ class EmailRepositoryImpl implements EmailRepository {
try {
await client.selectMailboxByPath(emailRow.mailboxPath);
final fetch = await client.uidFetchMessage(emailRow.uid, '(BODY.PEEK[])');
final msg = fetch.messages.first;
final msg = fetch.messages.firstOrNull;
if (msg == null) {
throw StateError(
'IMAP server returned no message for UID ${emailRow.uid}.',
);
}
final textBody = msg.decodeTextPlainPart();
final rawHtml = msg.decodeTextHtmlPart();
final htmlBody =
@@ -2812,7 +2817,12 @@ class EmailRepositoryImpl implements EmailRepository {
emailRow.uid,
'BODY.PEEK[]',
);
final msg = fetch.messages.first;
final msg = fetch.messages.firstOrNull;
if (msg == null) {
throw StateError(
'IMAP server returned no message for UID ${emailRow.uid}.',
);
}
final part = msg.getPart(attachment.fetchPartId) ?? msg;
final bytes = part.decodeContentBinary();
if (bytes == null) {
@@ -2878,7 +2888,13 @@ class EmailRepositoryImpl implements EmailRepository {
emailRow.uid,
'BODY.PEEK[]',
);
return fetch.messages.first.renderMessage();
final msg = fetch.messages.firstOrNull;
if (msg == null) {
throw StateError(
'IMAP server returned no message for UID ${emailRow.uid}.',
);
}
return msg.renderMessage();
} finally {
await client.logout();
}
+11
View File
@@ -163,6 +163,17 @@ class _EmailMessageCardState extends ConsumerState<_EmailMessageCard> {
FutureBuilder<EmailBody>(
future: _bodyFuture,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Padding(
padding: const EdgeInsets.all(16),
child: Text(
'Failed to load email: ${snapshot.error}',
style: TextStyle(
color: Theme.of(context).colorScheme.error,
),
),
);
}
if (!snapshot.hasData) {
return const Center(
child: Padding(