https://github.com/python/cpython/commit/3ccb1f8835fbd556cdb30a6be1411b04cc4f7c1a
commit: 3ccb1f8835fbd556cdb30a6be1411b04cc4f7c1a
branch: main
author: Shadi Bahaa <[email protected]>
committer: ethanfurman <[email protected]>
date: 2026-09-01T10:37:22-07:00
summary:

gh-149578: Fix tarfile.open failing on PAX archives with only global headers 
(GH-149647)

When a PAX format tar archive contains only global headers and no
regular members, tarfile.open() raised ReadError because the
EOFHeaderError from reaching end-of-archive after the global header
was being caught and converted to SubsequentHeaderError in
TarInfo._proc_pax(). This prevented the caller from handling
end-of-archive normally.

Fix by letting EOFHeaderError propagate when processing a global
header (XGLTYPE), while still treating it as a SubsequentHeaderError
for extended headers (XHDTYPE) where a following file entry is
mandatory.

Co-authored-by: Ethan Furman <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-05-10-12-00-00.gh-issue-149578.5cgq7iV.rst
M Lib/tarfile.py
M Lib/test/test_tarfile.py

diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index 451302715329fe..f46e938fd314dd 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -1601,6 +1601,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 3899eac6be3b3a..10106c3ada9ba5 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -2499,6 +2499,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.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to