Uh oh!
There was an error while loading. Please reload this page.
feat(install): diagnose a blocked package index instead of retrying blindly - #439
Merged
Jason Robert (jrob5756) merged 3 commits intoAug 14, 2026
Merged
Conversation
…lindly On networks that block direct access to the public Python package index -- increasingly common on managed corporate devices -- `uv tool install` fails with a fetch/403/DNS-shaped error. uv has already exhausted its own retries by that point, so the install scripts spent their 2s/5s/10s backoff on a failure that cannot heal and then printed file-lock advice, including a Windows Defender exclusion suggestion that is actively misleading for a network-policy block. Both scripts now classify the failure (`is_network_block_error` / `Test-NetworkBlockError`), stop after the first attempt, and explain the actual remedy: point uv at your organization's index with `UV_DEFAULT_INDEX`. They also echo the active index at install time, so "did my override apply?" is answerable from the install log alone, and the Defender advice is now scoped to lock failures only. The needles are deliberately generic and no mirror is hardcoded. Conductor is public OSS installed on many different networks; a baked-in endpoint would silently redirect every other user's dependency resolution. The index stays user-supplied configuration. Also documents the setup in the README, including the trap that uv does not read pip's configuration -- so `pip config set global.index-url` alone has no effect on the install scripts, `uv tool install`, or `conductor update`. Tests: the `_uv_shim` gains a `network-always` mode and a POSIX shim, and a behavioral test asserts exactly one uv attempt plus the guidance text. Four static checks run in the default suite, including one that fails if either script ever hardcodes an index URL (matched by shape, so it catches a mirror the test's author never heard of) and one that keeps the scripts' docs link from dangling. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Follow-up to the blocked-index classifier, addressing findings from a multi-agent code review. Four were reproduced against real uv 0.11.30 and a faithful simulation of each script's retry loop before being fixed. A blocked github.com was reported as a blocked package index. The installer fetches Conductor itself from `git+https://github.com/...`, and uv words a failed git fetch with the same "failed to fetch" phrase it uses for the index. A user blocked from GitHub was told, in bold, that their package index was the problem -- so they would set a perfectly correct UV_DEFAULT_INDEX, fail identically, and conclude the URL was wrong. Git failures are now classified separately and get their own message, and they keep the retry schedule since uv does not retry git fetches internally. On Windows the index classifier was unreachable on a fresh install whose output also contained lock-shaped text. The lock fallback guarded on the *result* of the rename, and `Move-ConductorToolDirAside` returns $null when there is no existing tool dir -- so the guard never flipped, the branch `continue`d on all four attempts, and the run ended on the Defender advice this change exists to avoid. The guard now tracks the attempt. Classification read the accumulated log, so one transient blip anywhere in a run relabeled whatever the run finally failed on: a missing portaudio.h was reported as a blocked index. It now reads only the most recent attempt. The needles are split into two tiers. Only a definitive block (uv stating it gave up, or a policy/DNS/TLS fact) short-circuits the retries; connection-level failures still get the full schedule, since unlike a policy block a VPN blip can genuinely heal. Bare host needles are gone -- matching `pypi.org` alone reported a hash mismatch as an unreachable index, which is the worst available answer for a supply-chain signal. 401 is matched, `grep` is pinned to LC_ALL=C, and PowerShell lowercases invariantly. Also: the index echo redacts credentials (an index URL is a documented place to put them, and that line reaches every CI log); guidance adapts when an override is already set, rather than advising the user to do what they have already done; the named-index form is used throughout so the documented UV_INDEX_<NAME>_* credentials are actually usable; TLS-inspection and proxy remedies are named; a missing log no longer kills install.sh with a bare sed error; and the renamed-aside notice prints before any early exit. Tests: five new behavioral cases covering the negative direction (ordinary failure still retries and shows no index guidance), a transient blip, a block arriving on a later attempt, both classifiers matching at once, and a git-host failure. Each was verified to fail when its fix is reverted. The static test asserting the guidance mentions UV_DEFAULT_INDEX was vacuous -- the header comments satisfied it with every guidance function deleted -- so it now scopes to the function body, and the classifier test checks the function is called rather than merely defined. Shim env is cleared so a developer's own UV_DEFAULT_INDEX cannot satisfy an assertion, and an unknown shim mode fails loudly instead of falling through to a real network install. CHANGELOG corrected: the Defender advice was never rescoped (Write-Err just exits before it), and install.sh never had Defender advice to rescope. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Jason Robert (jrob5756)
marked this pull request as ready for review
August 14, 2026 19:59
…ge-index-guidance # Conflicts: # CHANGELOG.md # install.sh
Uh oh!
There was an error while loading. Please reload this page.
Jason Robert (jrob5756)
deleted the
feature/private-package-index-guidance
branch
August 14, 2026 20:53
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 freeto 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.
Problem
Installs fail on networks that block direct access to the public Python
package index — increasingly common on managed corporate devices, where a
policy requires all packages to come from an internal mirror.
uv tool installfails with a fetch/403/DNS-shaped error, and uv hasalready exhausted its own retries by that point. So the install scripts
spent their full 2s/5s/10s backoff on a failure that cannot heal, and
install.ps1then printed file-lock advice — including a suggestion to adda Windows Defender exclusion, which is useless and actively misleading
for a network-policy block.
The user-visible experience was three silent retries followed by wrong
advice. Nothing named the actual cause, and nothing named the setting that
fixes it.
What changed
Classification. Both scripts now sort a failed install into one of four
kinds and respond to each differently:
github.comconnection reset, timeout)The scripts also echo the active index at install time — with credentials in
the URL redacted — so "did my override apply?" is answerable from the
install log alone.
README.md— new Installing behind a proxy or private package indexsection: uv (env var,
uv.toml, named-index credentials, TLS inspection,proxies) and pip/pipx separately, including the trap that uv does not read
pip's configuration.
No vendor endpoint is hardcoded
Conductor is public OSS installed on many different networks. A baked-in
mirror would silently redirect every other user's dependency resolution, so
the index stays user-supplied configuration and the docs use a
<your-index-host>placeholder.A static test enforces this by shape rather than by denylisting known
vendors — it matches any
https://…/simple/URL and requires it to beeither
pypi.orgor the placeholder. That catches a mirror the test'sauthor never heard of, and avoids naming anyone's internal infrastructure
inside a public repo.
Second commit: review fixes
A multi-agent review found four issues that were reproduced against real uv
0.11.30 and a faithful simulation of each script's retry loop before being
fixed:
github.comwas reported as a blocked package index. uvwords a failed git fetch with the same "failed to fetch" phrase it uses
for the index, and the installer fetches Conductor itself from
git+https://github.com/...— so github.com is on the primary path forevery install. The user would set a perfectly correct
UV_DEFAULT_INDEX,fail identically, and conclude the URL was wrong.
whose output also contained lock-shaped text. The lock fallback guarded on
the result of the rename, and
Move-ConductorToolDirAsidereturns$nullwhen there is no existing tool dir — so the guard never flipped,the branch
continued on all four attempts, and the run ended on theDefender advice this PR exists to avoid.
anywhere in a run relabeled whatever the run finally failed on: a missing
portaudio.hwas reported as a blocked index.pypi.orgalone reported a hashmismatch as an unreachable index — the worst available answer for a
supply-chain signal. Bare host needles are gone; every needle now names a
failure.
Also in that commit: credentials redacted in the echo; guidance adapts when
an override is already set rather than advising what the user already did;
the named-index form throughout so the documented
UV_INDEX_<NAME>_*credentials are actually usable; TLS-inspection and proxy remedies named; a
missing log no longer kills
install.shwith a baresederror; therenamed-aside notice prints before any early exit;
401matched;greppinned to
LC_ALL=C; PowerShell lowercases invariantly; and the CHANGELOGcorrected (the Defender advice was never rescoped —
Write-Errjust exitsbefore it — and
install.shnever had Defender advice to rescope).Verification
Behavior was exercised end-to-end against a fake
uv, including the Windowspath under PowerShell 7.4.
install.shandinstall.ps1agree on all sevenscenarios:
Underlying uv behavior was confirmed against real uv 0.11.30 rather than
assumed:
UV_DEFAULT_INDEXredirects resolution; agit+https://…installroutes build deps (
packaging) through it too; the deprecatedUV_INDEX_URLstill works; and an unreachable git host really does emitfailed to fetch, which is what made the misdiagnosis above possible.Tests
_uv_shim.pygained seven modes and a POSIX shim (it was Windows-only).direction), transient blip, late block, both classifiers matching, and a
git-host failure.
make testsuite.Two tests were themselves found to be vacuous during review and fixed: the
guidance assertion was satisfied by the scripts' header comments with all
guidance functions deleted, and the classifier assertion passed for a
function that was defined but never called.
Full suite: 7064 passed; install-scripts 10 passed / 4 skipped; ruff and
tyclean;install.ps1still ASCII / no BOM / LF.Notes for reviewers
on a restricted network still supply their own index — by design.
remove the from-source build (and its build-backend fetches), but a
blocked network still needs the mirror.
Draft for review of approach before polish.