Skip to content

Conversation

@gaborbernat
Copy link
Member

@gaborbernat gaborbernat commented Jan 9, 2026

When running multiple tox processes in parallel (e.g., tox -p auto or multiple CI jobs), users encounter wheel corruption errors:

Failed to read dist/my_package-1.0.0-py3-none-any.whl
Invalid zip file 'dist/my_package-1.0.0-py3-none-any.whl': Could not find EOCD

Root Cause

The session view mechanism used hard links (os.link()) to create per-session copies of built wheels. Hard links share the same underlying file data (inode).

Example of the problem:

  1. Process A builds wheel → .tox/.tmp/package/1/pkg-1.0.whl (hard link to original)
  2. Process B starts reading from its session view
  3. Process A rebuilds the wheel (overwrites original file data)
  4. Process B's read fails - the file it's reading was corrupted mid-read

Even though numbered directories (.tmp/package/1/, .tmp/package/2/) prevent path collisions, all hard links point to the same inode, so modifying the original corrupts all views.

Solution

Replace os.link() with shutil.copyfile(), which creates an independent copy. On modern copy-on-write filesystems (APFS, Btrfs, XFS with reflink), this automatically uses reflink, making it nearly as fast as hard linking while maintaining data independence.

@gaborbernat gaborbernat changed the title fix: use copy instead of hard link for session package views fix: wheel corruption when running parallel tox processes Jan 9, 2026
Hard links share the same inode/data blocks as the original file.
When parallel tox processes rebuild a wheel, the original file gets
overwritten, corrupting what other processes are reading via their
hard-linked session views.

Using shutil.copyfile() creates an independent copy. On modern
filesystems (APFS, Btrfs, XFS), this automatically uses copy-on-write
(reflink) which is nearly as fast as hard linking.

Fixes wheel corruption errors ("Could not find EOCD") when running
multiple tox processes in parallel.

Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
@gaborbernat gaborbernat enabled auto-merge (squash) January 9, 2026 15:40
@gaborbernat gaborbernat merged commit 96658e0 into tox-dev:main Jan 9, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant