Skip to content

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

Open

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 error

The 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

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:341 check_target_guards runs refuse_uv_no_sources and refuse_uv_project_shadowing, but nothing for no-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 *-package lists). A sibling check for no-binary / no-binary-package naming the target, including uv's precedence (uv.toml over [tool.uv]) and the ancestor config lookup, would cover all three lanes.

Activity

  1. mikolalysenko commented on Oct 16, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged as priority:p1 (uv). Confirmed on main 657fce2: neither the vendored guard (vendor/pypi_uv.rs check_target_guards) nor the hosted wire-time guards (patch/redirect/mod.rs) look at no-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

  2. mikolalysenko commented on Oct 16, 2026

    @mikolalysenko
    CollaboratorAuthor

    [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.toml records six with only its sdist, although six 1.16.0 publishes a pure py2.py3-none-any wheel. The hosted scan replaces that entry with the patched-wheel archive (exit 0, only redirect_pypi_stale_install). The matching install then fails:

    echo six==1.16.0 > req.in
    uv pip compile -q req.in --no-binary six --format pylock.toml -o pylock.toml --python-version 3.11
    socket-patch scan --mode hosted --yes            # exit 0
    uv pip sync pylock.toml --no-binary six          # exit 2
    # error: Package `six` can't be installed because it is marked as `--no-binary` but is itself a binary distribution

    uv pip sync pylock.toml without the flag installs the patched wheel. So unlike the CLI-only uv pip install --no-binary case, 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

  3. mikolalysenko commented on Oct 21, 2026

    @mikolalysenko
    CollaboratorAuthor

    [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-d3cb3a


    Generated by Claude Code

  4. mikolalysenko commented on Oct 21, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Fix in progress: #1649


    Generated by Claude Code

  5. mikolalysenko commented on Oct 21, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] #1649 fixes the uv project, uv.toml and PEP 723 script lanes from the issue body: both modes now refuse before writing (vendored pypi_uv_no_binary, hosted redirect_uv_project_unsupported / redirect_uv_script_unsupported). It says Refs, not Fixes, because the hosted uv pip compile --no-binary pylock.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 the uv pip config 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions