gentle-flower-25372
07/16/2026, 3:19 AMgentle-flower-25372
07/16/2026, 3:23 AMCopy codeERROR: Failed to build '<https://aws>:****@<redacted>.<http://d.codeartifact.us-east-1.amazonaws.com/pypi/altana/simple/psycopg2-binary/2.9.12/psycopg2_binary-2.9.12.tar.gz|d.codeartifact.us-east-1.amazonaws.com/pypi/altana/simple/psycopg2-binary/2.9.12/psycopg2_binary-2.9.12.tar.gz>' when getting requirements to build wheel
brief-scientist-13682
07/16/2026, 3:43 AM-ldebug in CI so we can see command lines? If you can repro locally, you can both provide __run.sh - which has the command line and the pex pip download log, both in the retained sandbox dir.gentle-flower-25372
07/16/2026, 3:45 AMgentle-flower-25372
07/16/2026, 3:46 AMbrief-scientist-13682
07/16/2026, 3:46 AMgentle-flower-25372
07/16/2026, 3:47 AMgentle-flower-25372
07/16/2026, 3:47 AMbrief-scientist-13682
07/16/2026, 3:47 AMbrief-scientist-13682
07/16/2026, 3:48 AMbrief-scientist-13682
07/16/2026, 3:48 AMgentle-flower-25372
07/16/2026, 3:48 AMgentle-flower-25372
07/16/2026, 3:49 AMgentle-flower-25372
07/16/2026, 3:50 AMgentle-flower-25372
07/16/2026, 3:50 AMgentle-flower-25372
07/16/2026, 3:51 AMbrief-scientist-13682
07/16/2026, 3:51 AMgentle-flower-25372
07/16/2026, 3:51 AMgentle-flower-25372
07/16/2026, 3:51 AMbrief-scientist-13682
07/16/2026, 3:51 AMbrief-scientist-13682
07/16/2026, 3:51 AMbrief-scientist-13682
07/16/2026, 3:51 AMgentle-flower-25372
07/16/2026, 3:51 AMbrief-scientist-13682
07/16/2026, 3:51 AMgentle-flower-25372
07/16/2026, 3:51 AMbrief-scientist-13682
07/16/2026, 3:51 AMbrief-scientist-13682
07/16/2026, 3:51 AMbrief-scientist-13682
07/16/2026, 3:52 AMbrief-scientist-13682
07/16/2026, 3:52 AMbrief-scientist-13682
07/16/2026, 3:53 AMgentle-flower-25372
07/16/2026, 3:53 AMgentle-flower-25372
07/16/2026, 3:53 AMbrief-scientist-13682
07/16/2026, 3:55 AMgentle-flower-25372
07/16/2026, 3:55 AMbrief-scientist-13682
07/16/2026, 3:56 AMgentle-flower-25372
07/16/2026, 3:57 AMgentle-flower-25372
07/16/2026, 3:59 AM2.9.9 and NOT 2.9.12
I'm not crazy, right? lolgentle-flower-25372
07/16/2026, 4:01 AMNow the picture is complete and definitive:
- PyPI sandbox β experiment-psycopg2.lock was written (4861 bytes) = SUCCESS
- CodeArtifact sandbox β no lock file = FAILURE
β¦yet both pex-pip-download.log files end in Would install psycopg2-binary-2.9.12 with zero errors. So the failure happens in the pex universal-lock step that runs after pip download β which this log doesn't capture. Let me grab the actual pex error by re-running the CodeArtifact invocation (its token may still be valid β the run was earlier today).brief-scientist-13682
07/16/2026, 4:01 AMgentle-flower-25372
07/16/2026, 4:01 AMbrief-scientist-13682
07/16/2026, 4:02 AMbrief-scientist-13682
07/16/2026, 4:02 AMgentle-flower-25372
07/16/2026, 4:03 AMgentle-flower-25372
07/16/2026, 4:03 AMgentle-flower-25372
07/16/2026, 4:06 AMbrief-scientist-13682
07/16/2026, 4:07 AMbrief-scientist-13682
07/16/2026, 4:07 AMbrief-scientist-13682
07/16/2026, 4:07 AMgentle-flower-25372
07/16/2026, 4:08 AMbrief-scientist-13682
07/16/2026, 4:08 AMgentle-flower-25372
07/16/2026, 4:11 AMgentle-flower-25372
07/16/2026, 4:12 AMThe two pex-pip-download.log files are essentially identical β that's the trap
I diffed them every way that matters. For psycopg2-binary, both logs succeed the same way:
βββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β β CodeArtifact (j7VyQZ) β PyPI (JhtdIw) β
βββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ€
β Versions offered β 2.7.xβ2.9.12 (identical) β 2.7.xβ2.9.12 (identical) β
βββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ€
β cp312 wheels for 2.9.12 β identical 11 wheels β identical 11 wheels β
βββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ€
β Resolved β psycopg2-binary 2.9.12 β psycopg2-binary 2.9.12 β
βββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ€
β Last line β Would install psycopg2-binary-2.9.12 β Would install psycopg2-binary-2.9.12 β
βββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ€
β error/warning markers β 0 β 0 β
βββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ
The one real difference in the logs: PyPI serves PEP 658 metadata sidecars, so pip fetches a 4.9 kB ...whl.metadata file (Obtaining dependency information ... 5 .metadata refs). CodeArtifact advertises no PEP 658 metadata (0 .metadata refs), so pip downloads the full 3.4 MB wheel to read its metadata. That's a symptom, not the cause.
The actual failure isn't in this log at all
pex-pip-download.log only captures pip's resolve sub-step. The lock failure happens in the next pex step, which your full -ldebug run shows plainly:
- PyPI β Wrote lockfile ... experiment-psycopg2.lock β
(I confirmed the file exists in that sandbox; j7VyQZ has no lock)
- CodeArtifact β ProcessExecutionFailure: 'Generate lockfile for experiment-psycopg2' failed with exit code 1, at pex's fingerprint_artifact step:
pip download --no-deps .../psycopg2-binary/2.9.12/psycopg2_binary-2.9.12.tar.gz β the SDIST
Γ python setup.py egg_info did not run successfully.
Error: pg_config executable not found.
pg_config is required to build psycopg2 from source.
error: metadata-generation-failed
I re-ran the CodeArtifact __run.sh and reproduced this exactly β so it's not a stale-token fluke.
Why the sdist gets built against CodeArtifact but not PyPI
A --style=universal lock records every artifact of the locked version β including the .tar.gz sdist β each with a sha256. The successful PyPI lock proves it (the sdist is recorded with hash 5ac9444β¦ next to all the wheel hashes).
- PyPI hands pex the per-file hashes + PEP 658 core-metadata inline in the Simple API, so pex records the sdist's hash without ever downloading it.
- CodeArtifact exposes neither (the same deficiency you see as "no .metadata" in the download log), so pex is forced to download the sdist to fingerprint it β and pip download of an sdist runs its setup.py, which needs pg_config (libpq dev headers), absent on the lock host β the whole lock dies.
So: "besides the index URL" the download logs genuinely differ in nothing that fails β the divergence is that CodeArtifact's index is not hash/PEPβ658βcomplete, and that gap only bites downstream, on the sdist fingerprint.
Fix
Stop pex from fingerprinting/building the sdist β same knob that resolved the DXβ2101/DXβ2128 lockfile-gen outage:
[python]
resolves_to_only_binary = { experiment-psycopg2 = [":all:"] } # or = ["psycopg2-binary"]
With only-binary, the sdist isn't a lock candidate, nothing gets built, and it locks against CodeArtifact. (Installing pg_config on the lock host would also "work" but is the wrong fix for a binary-wheel package.) I saw you're already in pants.toml β that's the line.gentle-flower-25372
07/16/2026, 4:12 AMConfirmed β and I verified it against the actual version, not the clone's HEAD.
First, the version check: your clone was on v2.97.3, but the pex that Pants 2.30.0 actually ran is 2.66.0 β from the sandbox ./pex itself:
- PEX-INFO β "build_properties":{"pex_version":"2.66.0"}
- .deps/pex-2.66.0-py2.py3-none-any.whl/pex/version.py β __version__ = "2.66.0"
I checked out v2.66.0 in ~/pex and re-read the code (it's verbatim identical to 2.97.3, same line numbers). Your suspicion is exactly right. The chain:
1. Fingerprint comes from the index link's #sha256= fragment β pex/artifact_url.py:208-223:
fragment_parameters = parse_qs(url_info.fragment)
if fragment_parameters:
# Artifact URLs from indexes may contain pre-computed hashes. We isolate those here...
# See: <https://peps.python.org/pep-0503/#specification>
for alg in RANKED_ALGORITHMS:
hashes = fragment_parameters.pop(alg, None)
...
fingerprints.append(Fingerprint(algorithm=alg, hash=hashes[0]))
β¦and pex/artifact_url.py:268-270:
def fingerprint(self):
return self.fingerprints[0] if self.fingerprints else None # no #sha256= β None
2. That flows straight into the artifact β pex/resolve/locker.py:288:
partial_artifact = PartialArtifact(artifact_url, fingerprint=artifact_url.fingerprint)
3. The decision to download β pex/resolve/downloads.py:141-149:
def _to_file_artifact(self, artifact):
fingerprint = artifact.fingerprint
if fingerprint: # index gave a hash β done, no download
return SpawnedJob.completed(self._create_file_artifact(url, fingerprint, ...))
return self._download_and_fingerprint(url) # no hash β must download to hash it
4. Download-to-fingerprint runs pip on the artifact β downloads.py:117-139 β _download (109-115):
download_dir = safe_mkdtemp(prefix="fingerprint_artifact.", ...) # β your temp dir
...
return self.pip.spawn_download_distributions(requirements=[download_url], transitive=False, ...)
For a .tar.gz sdist, pip download prepares metadata (setup.py egg_info) β psycopg2 needs pg_config β metadata-generation-failed.
So it's precisely as suspected: PyPI puts #sha256= on every link, so fingerprint is set and pex never touches the sdist. CodeArtifact's Simple API serves those links without a usable hash fragment, so fingerprint is None, pex hits the else branch, downloads the sdist to hash it, and that download triggers the source build that dies on pg_config.
The extra confirmation the source gives: under --style=universal the sdist is always recorded as a fallback artifact even though a wheel was selected β which is why it gets fingerprinted at all, and why any package with a build-requiring sdist breaks against CodeArtifact (matching adbc/ddtrace from the earlier outage). resolves_to_only_binary drops the sdist from the artifact set, so there's nothing to fingerprint.brief-scientist-13682
07/16/2026, 4:13 AMYep, the actual pip download logs are identical,Cannot be true since you claim prior to incident sdist was not built.
brief-scientist-13682
07/16/2026, 4:13 AMgentle-flower-25372
07/16/2026, 4:14 AMbrief-scientist-13682
07/16/2026, 4:14 AMbrief-scientist-13682
07/16/2026, 4:14 AMgentle-flower-25372
07/16/2026, 4:14 AMbrief-scientist-13682
07/16/2026, 4:14 AMbrief-scientist-13682
07/16/2026, 4:16 AMSo it's precisely as suspected: PyPI puts #sha256= on every link, so fingerprint is set and pex never touches the sdist. CodeArtifact's Simple API serves those links without a usable hash fragment, so fingerprint is None, pex hits the else branch, downloads the sdist to hash it, and that download triggers the source build that dies on pg_config.brief-scientist-13682
07/16/2026, 4:17 AMgentle-flower-25372
07/16/2026, 4:18 AMgentle-flower-25372
07/16/2026, 4:22 AMgentle-flower-25372
07/16/2026, 4:25 AM[python.resolves_to_only_binary] is almost certainly wrong here, correct? We haven't used that in 2 years of using pants so I wouldn't expect it all of a sudden, but I just wanted to ask πbrief-scientist-13682
07/16/2026, 4:26 AMbrief-scientist-13682
07/16/2026, 4:27 AMgentle-flower-25372
07/21/2026, 4:33 AM[python.resolves_to_only_binary] "fixes" it but it also dramatically explodes the amount of time the resolver takes.gentle-flower-25372
07/21/2026, 4:29 PMgentle-flower-25372
07/21/2026, 5:04 PMgentle-flower-25372
07/21/2026, 5:17 PM@brief-scientist-13682 β proxy experiment done, theory confirmed, and I now have the full mechanism. Everything below is verifiable against pex source; line numbers are v2.66.0 (the relevant files are unchanged through 2.98.2 β 2.93.3/.4 only added FIPS guards β and I reproduced on 2.98.2 too).
1. The trigger, proven by A/B. I put a ~40-line localhost proxy in front of our CodeArtifact repo that rewrites pip'sheader toAccepton simple-index pages and passes everything else through. Same pex 2.66.0, same pip 24.2, same repo,text/html, `psycopg2-binary==2.9.12`:--style universal
β’ direct (pip negotiates PEP 691 JSON since the rollout) βmetadata-generation-failed: pg_config executable not found
β’ through the proxy (forced PEP 503 HTML) β lock succeeds, sdist recorded with sha256, and pex'sdir is empty β zero fingerprint downloadsdownloads/
So nothing was nuked and no hashes were dropped: CodeArtifact's HTML still carriesfragments on every link (why it worked for 2 years, and why forcing HTML works today). What changed is which format pip receives β pip β₯22.2 always sends#sha256=, and CodeArtifact only just started honoring it. There's no pip flag to turn that off; the header is hardcoded inAccept: application/vnd.pypi.simple.v1+json, β¦+html;q=0.1, text/html;q=0.01._get_simple_response
2. Why JSON flips pex into download-everything mode. Per PEP 691 the JSONcarries no hash fragment β hashes move to the per-fileurlobject. Sohashesis None for every artifact andArtifactURL.fingerprintfalls into_to_file_artifact, exactly as you suspected. (This also reconciles the "identical pip logs" confusion: the resolve step picks the wheel in both eras and never touches the sdist β the sdist build happens in the separate per-artifact_download_and_fingerprintfingerprint invocations afterwards.)pip download --no-deps <sdist-url>
But pex has machinery for this case:records PEP-691-capable endpoints by scraping pip'sLockerlog lines (locker.py:531-537), thenFetched page <url> as application/vnd.pypi.simple.v1+json(locker.py:641) re-queries the JSON API for theFingerprintService.fingerprint(endpoints=β¦)dicts before any download fallback.hashes
3. The actual pex bug: that FingerprintService can't authenticate to CodeArtifact β two independent ways.
β’ (a) It inherits pip's log redaction. Our index URL carries basic auth (). pip logs page URLs through<https://aws|aws>:<token>@<domain>-<acct>.d.codeartifact.<region>.<http://amazonaws.com/pypi/|amazonaws.com/pypi/><repo>/simple, so the endpoint pex scrapes literally containsredact_auth_from_url. With `PEX_VERBOSE=3`:aws:****
pex: Fetching PEP-691 index metadata from <https://aws|aws>:****@<host>/pypi/<repo>/simple/<project>/ for application/vnd.pypi.simple.v1+json
β it authenticates with the literal passwordβ 401.****
β’ (b) Even with a clean endpoint URL and creds available (netrc; and create.py:304-308 does wireinto the service's URLFetcher), urllib can't complete the request: CodeArtifact answers 401 withpassword_entries, and urllib raisesWWW-Authenticate: Bearer
ValueError: AbstractDigestAuthHandler does not support the following scheme: 'Bearer'
Repro is one line:with creds inURLFetcher().get_body_stream("https://<host>/pypi/<repo>/simple/<project>/"). Looks like the same family as #1803/#1811, still alive on this path.~/.netrc
β’ Every failure is swallowed by(fingerprint_service.py:132-134), so the service degrades silently to download+fingerprint for all artifacts. That silence is what made this so hard to diagnose.catch(self._api.request, endpoint)
4. Why the blast radius was "everything, at once". Universal locks record the sdist as a fallback artifact for ~every project. Pre-rollout, all of them were fingerprinted for free off HTML fragments; post-rollout, every one gets downloaded, and pip β₯22 runs PEP-517 metadata prep on each β so any sdist that can't build on the lock host kills the whole lock: pg_config for psycopg2-binary, missing rust/native toolchains for others, and old `setup.py`s doing, which is freshly fatal because build isolation installs latest setuptools and β₯81 removed pkg_resources. Two-year-old pins "suddenly compiling" was never about the pins.import pkg_resources
5. Asks. Would you take an issue (I'll file it with full token-redacted PEX_VERBOSE traces) for:
1. re-attaching known credentials from the PasswordDatabase to scraped PEP-691 endpoints instead of the redacteduserinfo,****
2. toleratingon 401 (fall back to Basic when creds are known),WWW-Authenticate: Bearer
3. a warning instead of a silent download fallback when every PEP-691 fingerprint request fails?
Fixing those would make pex lock creation zero-download against CodeArtifact JSON again. Happy to test patches against our repo. Meanwhile we're unblocked via the Accept-rewrite proxy for regens,threaded throughPIP_CONSTRAINT=setuptools<81, and_PEX_USE_PIP_CONFIG=truefor the never-buildable sdists β and credit where due: only_binary as a debugging tool is what isolated the sdist path.resolves_to_only_binary
gentle-flower-25372
07/21/2026, 7:12 PMgentle-flower-25372
07/21/2026, 7:13 PMbrief-scientist-13682
07/31/2026, 1:34 AM