jstedfast/MailKit · error · MessageNotFoundException
The IMAP server did not return the requested body part.
Error message
The IMAP server did not return the requested body part.
What it means
MailKit throws MessageNotFoundException when it requested a BODY[] section from the IMAP server but the server's FETCH response did not include that section, so TryGetSection fails while assembling the ChainedStream for the returned entity. This means the server omitted a body part the library explicitly asked for, typically because the message vanished or the part specifier was not understood.
Solutions
- Check the message still exists (getMessageCount / re-fetch the summary) and retry the GetBodyPart call; treat MessageNotFoundException as 'message no longer available'.
- Catch MessageNotFoundException and skip the message or fall back to fetching the whole message with GetMessage and locating the entity locally.
- Verify the part specifier (e.g. '1.2', 'TEXT') against the BodyPart structure obtained from GetBody/ExamineBodyStructure; request a specifier the server actually reported.
- If a specific server reproducibly omits sections, update MailKit or the server firmware; some servers have known FETCH bugs.
Example fix
// before
var entity = folder.GetBodyPart(uid, part);
// after
MimeEntity entity;
try {
entity = folder.GetBodyPart(uid, part);
} catch (MessageNotFoundException) {
// message was expunged or server omitted the section
entity = null;
} Defensive patterns
Strategy: try-catch
Validate before calling
var summary = folder.GetSummary(uid, MessageSummaryItems.Body | MessageSummaryItems.UniqueId);
if (summary == null || !uid.IsValid) throw new InvalidOperationException("message not present"); Type guard
static bool CanFetch(ImapFolder folder, UniqueId uid) => folder != null && folder.OpenCount > 0 && uid.IsValid;
Try / catch
try { return folder.GetBodyPart(uid, part); }
catch (MessageNotFoundException) { return null; /* message gone / section omitted */ } Prevention
- Re-check message existence after any concurrent expunge risk
- Use UIDVALIDITY-aware UID caching
- Verify part specifiers against the message's body structure
- Keep MailKit and the IMAP server updated for known FETCH bugs
When it happens
Trigger: Calling ImapFolder.GetBodyPart(uid|index, partSpecifier) (or the BodyPart overloads / async variants) when the message was expunged by another client between the fetch request and the response, or when the server silently omits the requested BODY[part] section from its reply.
Common situations: Race with concurrent expunge (message deleted in another session or by server-side retention policy); servers that drop BODY[] sections for malformed or unsupported part specifiers; fetching a part of a message that was moved out of the folder; flaky/simplified IMAP servers that respond incompletely.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- The ImapFolder does not support mod-sequences.
- The IMAP server does not support the PARTIAL extension.
- The set of unique identifiers is too large to fetch with a…
- The PARTIAL extension only supports UID-based FETCH…
- The IMAP server did not return the requested message…
AI-assisted analysis of jstedfast/MailKit@9d3859a785 (2026-09-15).
Data as JSON: /api/errors/1e9e334a82823613.
Report an issue: GitHub.
Appendix: source
Thrown at MailKit/Net/Imap/ImapFolderFetch.cs:3610
ic.RegisterUntaggedHandler ("FETCH", FetchStreamHandler);
ic.UserData = ctx = new FetchStreamContext (progress);
Engine.QueueCommand (ic);
return ic;
}
void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid, string[] tags, out ChainedStream chained, out bool dispose)
{
ProcessFetchResponse (ic);
chained = new ChainedStream ();
dispose = false;
try {
foreach (var tag in tags) {
if (!ctx.TryGetSection (uid, tag, out var section, true))
throw new MessageNotFoundException ("The IMAP server did not return the requested body part.");
if (!(section.Stream is MemoryStream || section.Stream is MemoryBlockStream))
dispose = true;
chained.Add (section.Stream);
}
} catch {
chained.Dispose ();
throw;
}
}
void RemoveMessageHeaders (MimeEntity entity)
{
for (int i = entity.Headers.Count; i > 0; i--) {
var header = entity.Headers[i - 1];
if (!header.Field.StartsWith ("Content-", StringComparison.OrdinalIgnoreCase))View on GitHub (pinned to 9d3859a785)