diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index 792072ab9f6128a..7559a3389a5b26f 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -1668,6 +1668,11 @@ def get_domain(value): domain[:] = domain[0] while value and value[0] == '.': domain.append(DOT) + # A trailing dot leaves get_atom an empty string, whose error + # ("expected atext but found ''") hides the real problem. + if len(value) == 1: + raise errors.HeaderParseError( + "expected atom after '.' but found end of domain") token, value = get_atom(value[1:]) domain.append(token) return domain, value diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index 9d9fe418ee4d067..17520918c2a57a7 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -1501,6 +1501,13 @@ def test_get_domain_no_atom_raises(self): with self.assertRaises(errors.HeaderParseError): parser.get_domain(" (foo)\t, broken") + def test_get_domain_obsolete_trailing_dot_raises(self): + # gh-101034: a trailing dot must not surface get_atom's internal + # "expected atext but found ''" error. + with self.assertRaisesRegex(errors.HeaderParseError, + "expected atom after '.'"): + parser.get_domain("example.org.") + # get_addr_spec diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py index aa918255d15c37e..91a4bfbfd23c22a 100644 --- a/Lib/test/test_email/test_headerregistry.py +++ b/Lib/test/test_email/test_headerregistry.py @@ -1596,6 +1596,13 @@ def test_bad_addr_sepc_raises(self): with self.assertRaises(ValueError): Address('foo', addr_spec="name@ex[]ample.com") + def test_trailing_dot_in_addr_spec_domain_raises(self): + # gh-101034: report a clear error instead of the internal + # "expected atext but found ''". + with self.assertRaisesRegex(errors.HeaderParseError, + "expected atom after '.'"): + Address('Jane Doe', addr_spec="jane_doe@example.org.") + def test_empty_group(self): g = Group('foo') self.assertEqual(g.display_name, 'foo') diff --git a/Misc/NEWS.d/next/Library/2026-06-23-08-06-58.gh-issue-101034.VYZ3dP.rst b/Misc/NEWS.d/next/Library/2026-06-23-08-06-58.gh-issue-101034.VYZ3dP.rst new file mode 100644 index 000000000000000..2a3df24d6891799 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-23-08-06-58.gh-issue-101034.VYZ3dP.rst @@ -0,0 +1,4 @@ +Fix the misleading ``HeaderParseError`` raised for an email address whose +domain ends with a dot (such as ``user@example.org.``) in +:mod:`email.headerregistry`. It now reports a clear error instead of the +internal ``expected atext but found ''``.