Conversation
On 2026-09-16 six CI jobs across five pull requests and main died in the
dependency-install step, none of them on a red test:
ERROR: Could not install packages due to an OSError:
('Connection broken: IncompleteRead(214027 bytes read, 3458 more expected)')
Every one went green on a rerun of the same commit. The byte counts say what
it is: identical across the jobs of a single run (#57: both 214027/3458; #54:
both 230547/273), different between runs. One upstream response is cut and
whichever jobs are installing at that moment die together.
pip's own retries do not cover it. Every site already runs with pip's defaults
and setup-python's pip cache; those retries cover a connection that fails to
open, while a body cut mid-download surfaces from the install phase as OSError
and takes the process down. The unit to retry is the install.
tools/pip_install_retry.py retries ONLY the measured transport signatures. A
resolution error, a missing extra or a build failure still fails on the first
attempt, in seconds -- a real break must not hide behind three attempts and a
timeout. It lives in tools/ rather than as five copies of a shell loop because
inline workflow logic cannot be tested, the same reason ci_main_status.mjs is
there.
Wired into all five install steps (four in ci.yml, one in pages.yml).
Upgrading pip itself is left alone, so the retry cannot mask a broken pip.
tests/test_pip_install_retry.py drives the tool against a real child process,
including the branch that matters most -- a genuine failure is NOT retried --
and gates the wiring in both directions, so CI cannot silently stop calling it.
bd ahx
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
bd syntgrid-ahx. CI jobs keep dying in the dependency-install step on a cut package fetch, and a red check that no change caused trains readers to rerun rather than read.
tools/pip_install_retry.pyretries an install only when the failure is a transport error, and the five install steps inci.ymlandpages.ymlnow route through it.The measurement
Six failures on 2026-09-16, none of them a failing test, every one green on a rerun of the same commit:
main@ 39df523The byte counts identify the cause: identical across the jobs of one run, different between runs — PR #57 both jobs
IncompleteRead(214027 bytes read, 3458 more expected), PR #54 both230547/273,main9580/278. One upstream response is cut and whichever jobs are installing at that moment die together, so it is the fetch, not any job.Why pip's own retries do not cover it: every site already runs with pip's defaults and
setup-python'scache: pip. Those cover a connection that fails to open; a body cut mid-download surfaces from the install phase asOSError. The unit that has to be retried is the install.What it deliberately does not do
It does not retry a resolution error, a missing extra, or a build failure — those are this repository's bugs and must fail on the first attempt, in seconds.
TRANSIENT_MARKERSis a narrow, stated list of transport failures, and a test asserts no resolver wording can enter it.Upgrading pip itself is left as a plain
pip install --upgrade pip, so the retry cannot mask a broken pip.Why a tool rather than a shell loop in the YAML
Five call sites would be five copies of the same loop, and inline workflow logic cannot be tested — the same reason
tools/ci_main_status.mjsexists.tests/test_pip_install_retry.pydrives every branch against a real child process, not a mock, since what the tool classifies is a subprocess's combined output.Verification
python tools/pip_install_retry.py --attempts 2 -- --dry-run --no-deps --quiet wheelexits 0, and an invocation with nothing to install is rejected by argparse.test_mypy_ratchet,test_ci_main_status), both YAML files parse, and the docs/hygiene gates pass withtools/README.mdupdated to thirteen scripts plus its index row.Not in this PR
The deeper option — baking the dependency set into the self-hosted runner image — is not taken: that image rebuilds only when its
Dockerfilechanges, so the baked environment would go stale on everypyproject.tomledit. Its own header states the principle it follows ("the runtime a workload needs belongs in the image that runs it") for the OS-level runtime, which is a different question from Python packages that change with the tree.🤖 Generated with Claude Code