diff --git a/Lib/email/message.py b/Lib/email/message.py index 64e6c1e23cfb47f..093b9012f31666e 100644 --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -1026,7 +1026,7 @@ def _find_body(self, part, preferencelist): if subtype in preferencelist: yield (preferencelist.index(subtype), part) return - if maintype != 'multipart' or not self.is_multipart(): + if maintype != 'multipart' or not part.is_multipart(): return if subtype != 'related': for subpart in part.iter_parts(): diff --git a/Lib/test/test_email/test_message.py b/Lib/test/test_email/test_message.py index 56ad446694d1f88..974b0ceaafbde4e 100644 --- a/Lib/test/test_email/test_message.py +++ b/Lib/test/test_email/test_message.py @@ -1081,6 +1081,25 @@ def test_get_body_malformed(self): # AttributeError: 'str' object has no attribute 'is_attachment' m.get_body() + def test_get_body_malformed_nested_multipart(self): + # gh-159304: a nested multipart part without a boundary has a + # string payload, which get_body() must not iterate over. + msg = textwrap.dedent("""\ + From: sender@example.com + To: receiver@example.com + Subject: Malformed + Content-Type: multipart/related; boundary=OUTER + + --OUTER + Content-Type: multipart/related + + raw body no boundary + --OUTER-- + """) + m = self._str_msg(msg) + self.assertIsNone(m.get_body(('plain', 'html'))) + self.assertIs(m.get_body(('related',)), m) + def test_get_bytes_payload_with_quoted_printable_encoding(self): # We use a memoryview to avoid directly changing the private payload # and to prevent using the dedicated paths for string or bytes objects. diff --git a/Misc/NEWS.d/next/Library/2026-10-13-12-00-00.gh-issue-159304.mFbYq2.rst b/Misc/NEWS.d/next/Library/2026-10-13-12-00-00.gh-issue-159304.mFbYq2.rst new file mode 100644 index 000000000000000..8f5b61efe039593 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-13-12-00-00.gh-issue-159304.mFbYq2.rst @@ -0,0 +1,2 @@ +Fix :exc:`AttributeError` in :meth:`email.message.EmailMessage.get_body` when a +nested ``multipart`` part is malformed and its payload is a string.