From cfbb604b68929bbf15731c1766fb52dfa63265f8 Mon Sep 17 00:00:00 2001 From: Matt Van Horn Date: Thu, 21 May 2026 03:02:15 -0700 Subject: [PATCH] fix(pop3): wrap underlying errors (#1320) ## What? `findAttachmentData` in `backend/pop3/pop3.go` had three error sites that dropped underlying context: - `gomail.CreateReader` returned an error but the wrapping call discarded it with a static `"not a multipart message"` string. - The `mr.NextPart()` loop broke on a non-EOF error without saving it, so the final return at the bottom of the function reported "not found" instead of the real read failure. - The not-found error had no count of how many parts the loop actually scanned, which made the failure hard to debug from a log line alone. This PR wraps the `CreateReader` error with `%w`, captures the `NextPart` error in a local `scanErr` and surfaces it as the final error when it is set, and includes the scanned-parts count in the not-found message. ## Why? Fixes #688. The reporter noted the missing `%w` wrapping at the original line 438. While poking at that site I noticed the silent `break` on `mr.NextPart()` errors right above it. Those two paths share the same downstream "not found" return, so a multipart parse error in the middle of the stream looks identical to a genuinely missing part. The third change (scanned-parts count) is the smallest possible nudge that makes the two failure modes distinguishable in an error string without changing the return type or call sites. Fixes #688 --- backend/pop3/pop3.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/pop3/pop3.go b/backend/pop3/pop3.go index a883cf347e76ab35cba049188dca142d4db28246..4ec52040a3e9164c52fa9a8fe0c8d67b7f16c48f 100644 --- a/backend/pop3/pop3.go +++ b/backend/pop3/pop3.go @@ -465,16 +465,18 @@ func parseMessageBody(r io.Reader) (string, string, []backend.Attachment, error) func findAttachmentData(r io.Reader, targetPartID string) ([]byte, error) { mr, err := gomail.CreateReader(r) if err != nil { - return nil, fmt.Errorf("not a multipart message") + return nil, fmt.Errorf("pop3: not a multipart message: %w", err) } partIdx := 0 + var scanErr error for { part, err := mr.NextPart() if err == io.EOF { break } if err != nil { + scanErr = err break } partIdx++ @@ -484,5 +486,9 @@ func findAttachmentData(r io.Reader, targetPartID string) ([]byte, error) { } } - return nil, fmt.Errorf("pop3: attachment part %s not found", targetPartID) + if scanErr != nil { + return nil, fmt.Errorf("pop3: failed to scan attachment parts: %w", scanErr) + } + + return nil, fmt.Errorf("pop3: attachment part %s not found (scanned %d parts)", targetPartID, partIdx) }