Repository navigation
Hosted and vendored uv scans wire a patched wheel into a project that sets no-binary / no-binary-package for that package, so every later uv sync fails ("marked as --no-binary but is itself a binary distribution") #1603
Description
[agent] Found by the scheduled uv bug-hunt routine (ledger #310).
Summary
uv's no-binary = true and no-binary-package = ["<name>"] settings (in [tool.uv], uv.toml or a PEP 723 script's # [tool.uv]) forbid installing that package from a wheel. Every socket-patch PyPI artifact is a wheel: hosted mode pins the patch-server .whl URL, and vendored mode pins .socket/vendor/pypi/<uuid>/<name>-<ver>-py*-none-any.whl. When the patched package falls under one of these settings, scan --mode hosted and scan --mode vendored still wire it and exit 0 with no warning. After that, every install of the project fails:
error: Distribution `colorama==0.4.4 @ direct+http://…/colorama-0.4.4-py2.py3-none-any.whl` can't be installed because it is marked as `--no-binary` but is itself a binary distribution
This happens with uv sync --locked, uv sync --frozen, a plain uv sync and uv run --locked on a script. On uv 0.12+ uv lock --check also fails ("Failed to validate existing lockfile").
Impact
After a "successful" scan, CI and every fresh checkout can't install the project at all. The tool knows these keys exist: hosted rollback already refuses on any [tool.uv] no-binary* key (upstream/uv.rs:371), but nothing checks them at wire time. It's the same shape as #564 (no-sources), which was fixed by a pre-write refusal.
Repro
This uses the bughunt mock patch server (six / colorama patches; see the ledger) on 127.0.0.1:8765. Any free pure-Python patch shows the same thing.
mkdir nb && cd nb
cat > pyproject.toml <<'EOF'
[project]
name = "demo"
version = "0.1.0"
requires-python = ">=3.9"
dependencies = ["colorama==0.4.4", "attrs==24.2.0"]
[tool.uv]
no-binary-package = ["colorama"]
EOF
uv lock && uv sync # ok: colorama built from its sdist
socket-patch scan --mode hosted --json # or --mode vendored → exit 0, colorama wired
rm -rf .venv && uv sync --locked # exit 1 (0.13.1) / 2 (≤0.8): "marked as --no-binary but is itself a binary distribution"
uv sync --frozen # same errorThe same thing happens with no-binary = true, with no-binary-package in a uv.toml beside the project, and in a PEP 723 script (# [tool.uv] / # no-binary-package = ["colorama"], then uv lock --script s.py, scan, uv run --locked s.py exits 2).
Expected vs actual
- Expected: a refusal before any write, the way
no-sourcesis refused withpypi_uv_no_sources(Hosted and vendored uv wiring ignoreno-sources = true: scan and vendor report success,uv sync --lockedthen fails, and a plainuv syncreinstalls the unpatched wheel #564 / Fix uv wiring under no-sources = true (#564) #1434), because uv will never install a wheel for this package. Vendored mode's contract is that the nextuv sync --lockedinstalls the patched wheel, and hosted mode's is that the redirected URL is what the next install fetches. CLI_CONTRACT.md's hosted-restore rules already list "a uv lock whose options filter files (exclude-newer,no-binary,no-build)" as a setting the tool can't safely edit around. - Actual: scan exits 0 (
redirect_pypi_stale_install/vendor_prebuilt_downloadedonly), and the project can no longer be installed.
no-build-package / no-build are fine here: a wheel is allowed, and uv sync --locked passes. A no-binary-package that names a different package is fine too (control).
Matrix (Linux)
| uv | setting location | hosted | vendored |
|---|---|---|---|
| 0.5.31 | [tool.uv] no-binary-package |
fail | fail |
| 0.5.31 | [tool.uv] no-binary = true |
fail | fail |
| 0.5.31 | uv.toml no-binary-package |
fail | fail |
| 0.8.17 | [tool.uv] both keys |
fail | fail |
| 0.13.1 | [tool.uv] both keys / uv.toml |
fail | fail |
| 0.13.1 | PEP 723 script # [tool.uv] |
fail | fail |
| 0.5.31 / 0.13.1 | no-binary-package = ["attrs"] (unrelated) |
pass | pass |
| 0.5.31 / 0.13.1 | no-build-package = ["colorama"] |
pass | pass |
Each failing cell reproduced twice, on main 657fce2 and on the v5.0.1 release binary (so it isn't a regression in the 5.0 line). macOS / Windows were not probed: uv's no-binary check is platform-independent.
Suspect code
- Vendored:
crates/socket-patch-core/src/vendor/pypi_uv.rs:341check_target_guardsrunsrefuse_uv_no_sourcesandrefuse_uv_project_shadowing, but nothing forno-binary*. - Hosted:
crates/socket-patch-core/src/patch/redirect/mod.rs:5938-5960(the same two guards, for project and script). - The settings readers are already there in
crates/socket-patch-core/src/utils/python_script.rs(refuse_uv_no_sources, which reads pyproject /uv.toml/ script metadata and*-packagelists). A sibling check forno-binary/no-binary-packagenaming the target, including uv's precedence (uv.tomlover[tool.uv]) and the ancestor config lookup, would cover all three lanes.
Activity
mikolalysenko commentedon Oct 16, 2026
[agent] Triaged as
priority:p1(uv). Confirmed on main657fce2: neither the vendored guard (vendor/pypi_uv.rscheck_target_guards) nor the hosted wire-time guards (patch/redirect/mod.rs) look atno-binary/no-binary-package; only the hosted restore path (upstream/uv.rs~366) does. Related to #1604 (uv file-filter settings) but a separate check, so not clustered. No open PR covers it.Generated by Claude Code
mikolalysenko commentedon Oct 16, 2026
[agent] The same wiring also happens on the pylock lane, where the filter is visible in the lock itself (main
657fce2, uv 0.11.32 and 0.13.1, 2 runs each).Since uv 0.9.16,
uv pip compile --no-binary six --format pylock.tomlrecords six with only its sdist, although six 1.16.0 publishes a purepy2.py3-none-anywheel. The hosted scan replaces that entry with the patched-wheelarchive(exit 0, onlyredirect_pypi_stale_install). The matching install then fails:uv pip sync pylock.tomlwithout the flag installs the patched wheel. So unlike the CLI-onlyuv pip install --no-binarycase, a sdist-only entry for a release with a pure wheel is a signal the scan could detect and refuse on. On uv ≤ 0.9.15 the pylock keeps both artifacts, and the same flags give no signal. The unwind side of this (rollback re-adds the wheel) is in #1613.Generated by Claude Code
mikolalysenko commentedon Oct 21, 2026
[agent] Claiming this issue (shared root cause: uv wire-time guards never check
no-binary/no-binary-package). Branch: agent/fix-uv-no-binary-wire-guard. Claim-ID: 2026-10-21T00:23:00Z-d3cb3aGenerated by Claude Code
mikolalysenko commentedon Oct 21, 2026
[agent] Fix in progress: #1649
Generated by Claude Code
Refuse uv no-binary before wiring a wheel
Name the package in the uv no-binary refusal
mikolalysenko commentedon Oct 21, 2026
[agent] #1649 fixes the uv project,
uv.tomland PEP 723 script lanes from the issue body: both modes now refuse before writing (vendoredpypi_uv_no_binary, hostedredirect_uv_project_unsupported/redirect_uv_script_unsupported). It saysRefs, notFixes, because the hosteduv pip compile --no-binarypylock.toml lane from the follow-up comment is still open. That lock records nothing of the filter in its data; the signal is the uv header command line and theuv pipconfig files, which #1614 parses for the restore side. That lane is a follow-up once #1614 lands, so this issue stays open for it.Generated by Claude Code