diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 1394a26f2096ff..6e30bbe0f4d39b 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -1565,6 +1565,14 @@ def _proc_pax(self, tarfile): # Fetch the next header. try: next = self._fromtarfile(tarfile, dircheck=False) + except EOFHeaderError: + if self.type == XGLTYPE: + # If this is a global header at the end of the archive + # (no regular members follow), let the EOFHeaderError + # propagate so the caller handles end-of-archive normally. + tarfile.offset = tarfile.fileobj.tell() - BLOCKSIZE + raise + raise SubsequentHeaderError("end of file header") from None except HeaderError as e: raise SubsequentHeaderError(str(e)) from None diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 192c948edc6056..2dffb313f650de 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -2297,6 +2297,35 @@ def test_pax_global_header(self): finally: tar.close() + def test_pax_global_header_empty_archive(self): + # An archive that contains only a global header and no regular + # members should be opened successfully (gh-149578). + pax_headers = {"foo": "bar"} + + # Create a PAX archive with global headers but no file entries. + with tarfile.open(tmpname, "w", format=tarfile.PAX_FORMAT, + pax_headers=pax_headers): + pass + + # Reading the archive should work and preserve global headers. + with tarfile.open(tmpname) as tar: + self.assertEqual(tar.pax_headers, pax_headers) + self.assertEqual(tar.getmembers(), []) + + # Appending to the archive should work. + with tarfile.open(tmpname, "a") as tar: + self.assertEqual(tar.pax_headers, pax_headers) + self.assertEqual(tar.getmembers(), []) + tar.addfile(tarfile.TarInfo("test")) + + # Verify the appended member is present and global headers + # are preserved. + with tarfile.open(tmpname) as tar: + self.assertEqual(tar.pax_headers, pax_headers) + members = tar.getmembers() + self.assertEqual(len(members), 1) + self.assertEqual(members[0].name, "test") + def test_pax_extended_header(self): # The fields from the pax header have priority over the # TarInfo. diff --git a/Misc/NEWS.d/next/Library/2026-05-10-12-00-00.gh-issue-149578.5cgq7iV.rst b/Misc/NEWS.d/next/Library/2026-05-10-12-00-00.gh-issue-149578.5cgq7iV.rst new file mode 100644 index 00000000000000..eb738df5beb0c5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-10-12-00-00.gh-issue-149578.5cgq7iV.rst @@ -0,0 +1,3 @@ +Fix :func:`tarfile.open` failing with :exc:`~tarfile.ReadError` when +opening a PAX format tar archive that contains only global headers and +no regular members.