Summary
When a --style universal lock is created against an index that requires Basic authentication and serves the PEP-691 JSON Simple API, Pex cannot authenticate the follow-up requests made by its PEP-691 FingerprintService. Pex warns and then falls back to downloading every retained artifact for each selected locked version just to fingerprint it. Callers such as Pants suppress Pex warnings, making the fallback operationally silent.
Because pip download --no-deps <direct-sdist-url> can run PEP-517 metadata hooks, retained sdists may then fail the lock for reasons unrelated to dependency resolution—for example, psycopg2-binary without pg_config, or ddtrace==3.19.1 importing pkg_resources after build isolation installs setuptools 82, which removed it. Even when every retained sdist can prepare metadata, lock creation needlessly downloads and hashes a potentially large artifact set.
This surfaced when AWS CodeArtifact began serving PEP 691 in July 2026. Pip >=22.2 prefers JSON through content negotiation, so locks that had regenerated successfully against PEP-503 HTML began failing without a client-side configuration change. With HTML, artifact URLs include #sha256= fragments and Pex obtains hashes directly from Pip's log; with JSON, hashes are separate files[].hashes values and are not included in Pip's logged URLs.
Root causes
1. PEP-691 endpoints are scraped from Pip's credential-redacted log
Locker.analyze records endpoints from Pip lines of this form:
Fetched page <url> as application/vnd.pypi.simple.v1+json
For an index configured as https://user:secret@host/simple, Pip logs the endpoint as literal https://user:****@host/simple/<project>/. Before the fix, Pex attempted to open that URL:
pex: Fetching PEP-691 index metadata from https://aws:****@<host>/pypi/<repo>/simple/<project>/ for application/vnd.pypi.simple.v1+json
The stdlib opener cannot parse its authority:
PEXWarning: Failed to fetch project metadata, continuing: nonnumeric port: '****@<host>'
Pex separately retains the real repository credential, but the redacted userinfo in the endpoint prevents that credential from being used.
2. Clean endpoint URLs still fail against CodeArtifact's challenge
With the redacted userinfo removed and a matching machine-scoped password entry supplied, the pre-fix URLFetcher still starts unauthenticated. CodeArtifact accepts Basic authentication but answers an unauthenticated request with 401 and WWW-Authenticate: Bearer .... Python's stdlib Basic/Digest challenge handlers do not support that scheme and can raise:
ValueError: AbstractDigestAuthHandler does not support the following scheme: 'Bearer'
As a result, the known Basic credential is never sent. This is in the same family as #1803, which #1811 fixed for artifact downloads by delegating them to Pip; the independently implemented PEP-691 metadata client retained the problematic stdlib authentication path.
FingerprintService.fingerprint intentionally treats these request errors as non-fatal, emits Failed to fetch project metadata, continuing, and leaves artifacts without fingerprints. Higher-level lock creation then downloads each unresolved retained artifact and hashes it locally.
Reproduction
Against a CodeArtifact repository with a public PyPI upstream, from a lock host without pg_config:
TOKEN=$(aws codeartifact get-authorization-token ... --query authorizationToken --output text)
pex3 lock create \
--pip-version 24.2 --style universal --no-pypi \
--index "https://aws:${TOKEN}@<domain>-<acct>.d.codeartifact.<region>.amazonaws.com/pypi/<repo>/simple" \
psycopg2-binary==2.9.12 -o lock.json
On v2.66.0 and main at bea9c2e, this fails with metadata-generation-failed / pg_config executable not found after downloading all 12 retained artifacts for the selected release.
The challenge failure can be reproduced without AWS by giving URLFetcher.get_body_stream a matching machine-scoped password entry and pointing it at a server that accepts Basic auth but returns 401 with WWW-Authenticate: Bearer when the initial request lacks authorization.
Expected behavior
PEP-691 metadata requests should authenticate using the known credential for the configured index, hashes should be recovered from the JSON files[].hashes mappings, and no retained artifacts should be downloaded solely for fingerprinting. This should produce the same lock artifact set and hashes as the equivalent PEP-503 HTML response with #sha256= fragments.
Fix
#3225 strips credentials from Pip-redacted endpoint URLs, sends known machine-scoped Basic credentials preemptively, safely handles unsupported challenge schemes, and adds composed regression coverage from the redacted Pip log line through authenticated PEP-691 SHA-256 recovery. It has been validated against the CodeArtifact repository that exposed the issue: the reproducer completes with zero fallback fingerprint downloads and produces hashes identical to the HTML-index lock.
Summary
When a
--style universallock is created against an index that requires Basic authentication and serves the PEP-691 JSON Simple API, Pex cannot authenticate the follow-up requests made by its PEP-691FingerprintService. Pex warns and then falls back to downloading every retained artifact for each selected locked version just to fingerprint it. Callers such as Pants suppress Pex warnings, making the fallback operationally silent.Because
pip download --no-deps <direct-sdist-url>can run PEP-517 metadata hooks, retained sdists may then fail the lock for reasons unrelated to dependency resolution—for example,psycopg2-binarywithoutpg_config, orddtrace==3.19.1importingpkg_resourcesafter build isolation installs setuptools 82, which removed it. Even when every retained sdist can prepare metadata, lock creation needlessly downloads and hashes a potentially large artifact set.This surfaced when AWS CodeArtifact began serving PEP 691 in July 2026. Pip >=22.2 prefers JSON through content negotiation, so locks that had regenerated successfully against PEP-503 HTML began failing without a client-side configuration change. With HTML, artifact URLs include
#sha256=fragments and Pex obtains hashes directly from Pip's log; with JSON, hashes are separatefiles[].hashesvalues and are not included in Pip's logged URLs.Root causes
1. PEP-691 endpoints are scraped from Pip's credential-redacted log
Locker.analyzerecords endpoints from Pip lines of this form:For an index configured as
https://user:secret@host/simple, Pip logs the endpoint as literalhttps://user:****@host/simple/<project>/. Before the fix, Pex attempted to open that URL:The stdlib opener cannot parse its authority:
Pex separately retains the real repository credential, but the redacted userinfo in the endpoint prevents that credential from being used.
2. Clean endpoint URLs still fail against CodeArtifact's challenge
With the redacted userinfo removed and a matching machine-scoped password entry supplied, the pre-fix
URLFetcherstill starts unauthenticated. CodeArtifact accepts Basic authentication but answers an unauthenticated request with401andWWW-Authenticate: Bearer .... Python's stdlib Basic/Digest challenge handlers do not support that scheme and can raise:As a result, the known Basic credential is never sent. This is in the same family as #1803, which #1811 fixed for artifact downloads by delegating them to Pip; the independently implemented PEP-691 metadata client retained the problematic stdlib authentication path.
FingerprintService.fingerprintintentionally treats these request errors as non-fatal, emitsFailed to fetch project metadata, continuing, and leaves artifacts without fingerprints. Higher-level lock creation then downloads each unresolved retained artifact and hashes it locally.Reproduction
Against a CodeArtifact repository with a public PyPI upstream, from a lock host without
pg_config:On v2.66.0 and
mainat bea9c2e, this fails withmetadata-generation-failed/pg_config executable not foundafter downloading all 12 retained artifacts for the selected release.The challenge failure can be reproduced without AWS by giving
URLFetcher.get_body_streama matching machine-scoped password entry and pointing it at a server that accepts Basic auth but returns401withWWW-Authenticate: Bearerwhen the initial request lacks authorization.Expected behavior
PEP-691 metadata requests should authenticate using the known credential for the configured index, hashes should be recovered from the JSON
files[].hashesmappings, and no retained artifacts should be downloaded solely for fingerprinting. This should produce the same lock artifact set and hashes as the equivalent PEP-503 HTML response with#sha256=fragments.Fix
#3225 strips credentials from Pip-redacted endpoint URLs, sends known machine-scoped Basic credentials preemptively, safely handles unsupported challenge schemes, and adds composed regression coverage from the redacted Pip log line through authenticated PEP-691 SHA-256 recovery. It has been validated against the CodeArtifact repository that exposed the issue: the reproducer completes with zero fallback fingerprint downloads and produces hashes identical to the HTML-index lock.