Skip to content

pip install --target --upgrade does not replace existing package versions #13763

@SupulHeshan

Description

@SupulHeshan

Description

According to pip install --help, the --upgrade flag with --target is documented as:

"Use --upgrade to replace existing packages in <dir> with new versions."

However, in practice, when using pip install --target <dir> --upgrade, existing package versions are not removed. Instead, the new version is added alongside the old one, resulting in multiple .dist-info directories for the same package.

This behavior leads to ambiguous and incorrect results when tools rely on importlib.metadata, which expects a single installed distribution per package.

Why This Matters

  • pip install --target is commonly used by:

    • language tooling
    • plugin systems
    • build tools
    • embedded runtimes
  • Multiple .dist-info directories for the same package:

    • break importlib.metadata
    • cause version resolution bugs
    • force downstream tools to implement manual cleanup logic

This makes --target installs unsafe for repeated upgrades unless users manually delete old package metadata.

Expected behavior

When using:

pip install <package> --upgrade --target <dir>

pip should remove:

  • older .dist-info directories
  • legacy .egg-info directories (if present)
  • associated package files (where applicable)

so that only one version of the package remains in the target directory, matching the behavior of standard (non---target) installs.

Expected directory contents::

typing_extensions.py
typing_extensions-4.12.2.dist-info/

pip version

25.3

Python version

python 3.13

OS

Windows

How to Reproduce

Steps to Reproduce

# Create a clean target directory
mkdir -p /tmp/pip-target-test

# Install an older version
pip install typing-extensions==4.8.0 --target /tmp/pip-target-test

# Install a newer version using --upgrade
pip install typing-extensions==4.12.2 --upgrade --target /tmp/pip-target-test

Output

Directory contents after both installs

$ ls /tmp/pip-target-test
typing_extensions.py
typing_extensions-4.8.0.dist-info
typing_extensions-4.12.2.dist-info

Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    S: needs triageIssues/PRs that need to be triagedtype: bugA confirmed bug or unintended behavior

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions