pypa/pip · error · InstallationError
Multiple locked links provided for {project_name}: {self._lo
Error message
Multiple locked links provided for {project_name}: {self._locked_links[project_name]} and {locked_link} What it means
InstallationError raised in add_locked_link when a second locked link is registered for a project name that already has one. The locked-link table must map one project to exactly one artifact; a duplicate means the lockfile/constraints source is internally inconsistent.
Source
Thrown at src/pip/_internal/index/package_finder.py:1103
logger.debug(
"Using version %s (newest of versions: %s)",
best_candidate.version,
_format_versions(best_candidate_result.applicable_candidates),
)
return best_candidate
# We have an existing version, and its the best version
logger.debug(
"Installed version (%s) is most up-to-date (past versions: %s)",
installed_version,
_format_versions(best_candidate_result.applicable_candidates),
)
raise BestVersionAlreadyInstalled
def add_locked_link(self, project_name: NormalizedName, locked_link: Link) -> None:
assert not self._all_candidates
if project_name in self._locked_links:
raise InstallationError(
f"Multiple locked links provided for {project_name}: "
f"{self._locked_links[project_name]} and {locked_link}"
)
self._locked_links[project_name] = locked_link
def _find_name_version_sep(fragment: str, canonical_name: str) -> int:
"""Find the separator's index based on the package's canonical name.
:param fragment: A <package>+<version> filename "fragment" (stem) or
egg fragment.
:param canonical_name: The package's canonical name.
This function is needed since the canonicalized name does not necessarily
have the same length as the egg info's name part. An example::
>>> fragment = 'foo__bar-1.0'View on GitHub (pinned to d7d0d0a394)
Solutions
- Inspect the lockfile/constraints file for duplicate entries for the named project (account for name normalization - 'Foo', 'foo', 'F-O-O' all canonicalize together).
- Keep a single pinned artifact per project in the lock and remove the duplicate.
- Regenerate the lockfile with a current lockfile tool to deduplicate automatically.
- If merging locks, resolve the conflict explicitly before installing.
Example fix
# before - locked-requirements.txt foo==1.0 --hash=sha:aaa Foo==1.1 --hash=sha:bbb # duplicate after canonicalization # after foo==1.0 --hash=sha:aaa
Defensive patterns
Strategy: validation
Validate before calling
from pip._internal.utils.misc import canonicalize_name
seen = {}
for project, link in locked_links:
c = canonicalize_name(project)
if c in seen:
print(f'duplicate lock entry for {c}: {seen[c]} vs {link}')
seen[c] = link Type guard
def no_duplicate_locks(locked_links) -> bool:
from pip._internal.utils.misc import canonicalize_name
names = [canonicalize_name(p) for p, _ in locked_links]
return len(names) == len(set(names)) Try / catch
from pip._internal.exceptions import InstallationError
try:
finder.add_locked_link(name, link)
except InstallationError as e:
if 'Multiple locked links' in str(e):
# deduplicate the lockfile by canonical name
... Prevention
- Canonicalize project names when generating lockfiles.
- Deduplicate lock entries before installing.
- Regenerate locks with a current tool to avoid normalization drift.
- Treat 'Foo' and 'foo' as the same project.
When it happens
Trigger: Triggered when code (lockfile loader / resolver) calls finder.add_locked_link(project_name, link) twice for the same canonical project_name with different Link objects. self._all_candidates is asserted empty, so it happens during the setup phase before any resolution.
Common situations: A lockfile or constraints file that pins two different files/URLs for the same package; a bug in a lockfile generator producing duplicate entries under different canonicalizations (e.g. 'Foo' and 'foo'); merging two lockfiles that both pin the same dependency differently.
Related errors
- Could not install locked package {project_name!r} from {lock
- No matching distribution found for {req}
- Editable requirements are not allowed as constraints
- Unnamed requirements are not allowed as constraints
- Editable requirements are not allowed as constraints
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/3f3f7751e47b8e4d.json.
Report an issue: GitHub.