GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Vendored uv `repair` can't rebuild a missing wheel once `uv sync` has installed it, because it rebuilds from the patched installed copy and the hash never matches · Issue #381 · SocketDev/socket-patch

Vendored uv repair can't rebuild a missing wheel once uv sync has installed it, because it rebuilds from the patched installed copy and the hash never matches #381

New issue
Copy link
New issue
Copy link
Closed
Closed
Vendored uv repair can't rebuild a missing wheel once uv sync has installed it, because it rebuilds from the patched installed copy and the hash never matches#381
Copy link
Labels
agent:triagedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:uvuvuvpriority:p1

Description

@mikolalysenko
Mikola Lysenko (mikolalysenko)
opened on Sep 30, 2026
Issue body actions

[agent] Found by the scheduled uv bug-hunt routine (ledger #310).

Summary

On a vendored uv project, socket-patch repair restores a deleted .socket/vendor/pypi/<uuid>/<wheel> fine on a lock-only checkout: it fetches the pristine wheel from the ledger's registry fragment and reports rebuilt. On the normal developer checkout, where uv sync has already run, it fails. In that case the local build path (crates/socket-patch-core/src/vendor/pypi.rs:1824, "Local build from the installed dist") is taken first. The installed dist is the patched, uv-installed copy of the vendored wheel (uv's INSTALLER, direct_url.json, rewritten RECORD), so the rebuilt wheel never reproduces the sha256 uv.lock pins:

vendor_artifact_rebuild_failed: the rebuilt wheel (.socket/vendor/pypi/<uuid>/six-1.16.0-py2.py3-none-any.whl, sha256 a1b9797e…) does not match the wheel the lockfile still pins (…, sha256 01663f71…); run `socket-patch vendor --revert` for pkg:pypi/[email protected] and re-vendor to re-wire the lockfile

The rebuilt sha is different on every run (a1b9797e…, 794ad96b…, fa13f665…, a2900bcf…, 071be102…), so retrying never helps. The run also removes the package's committed socket-patch.vendor.json sidecar (the documented "nothing kept" path). uv sync --locked then fails with failed to open file …six-1.16.0-py2.py3-none-any.whl.

Impact

The README describes repair as the command that "rebuilds missing/corrupt vendored artifacts". Here it only works if the user first deletes their virtualenv, which nothing tells them to do. The error points them to vendor --revert + re-vendor, which rewrites the lock unnecessarily. The case is common: any checkout where the wheel went missing (bad merge, LFS or .gitignore mishap) after uv sync.

Repro (Linux, uv 0.12.21)

Local mock patch API serving a patched six-1.16.0 wheel, as in the uv VEX e2e harness.

SP="socket-patch --api-url http://127.0.0.1:18080 --api-token t --org test-org"
mkdir proj && cd proj
printf '[project]\nname = "uvp"\nversion = "0.1.0"\nrequires-python = ">=3.9"\ndependencies = ["six==1.16.0"]\n' > pyproject.toml
printf '.venv\n' > .gitignore
uv lock && uv sync
$SP scan --mode vendored --json --yes        # applied: 1
git init -q && git add -A && git commit -qm vendored
cd .. && git clone -q proj clone && cd clone
uv sync --locked                             # installs the patched six.py ✔
rm .socket/vendor/pypi/*/six-1.16.0-py2.py3-none-any.whl
$SP repair --json                            # exit 1, failed vendor_artifact_rebuild_failed
uv sync --locked                             # fails: wheel file missing
# Control: the same steps WITHOUT the `uv sync --locked` in the clone -> repair "rebuilt", exit 0, --locked installs the patch.

Expected vs actual

  • Expected: repair rebuilds the committed wheel byte-for-byte, as it already does on a lock-only checkout. The ledger's wiring[].original fragment names the pristine PyPI wheel and its hash, so a verifiable pristine source exists. An installed copy that is itself the vendored (patched) wheel is not a pristine source and shouldn't be preferred over it.
  • Actual: vendor_artifact_rebuild_failed on every run, a non-deterministic rebuilt hash, and the vendor sidecar deleted.

OS × uv matrix (main f6b7fb9)

OS uv 0.2.37, venv present uv 0.12.21, venv present uv 0.2.37, lock-only (control) uv 0.12.21, lock-only (control)
Linux pass fail (3/3 runs) pass pass
macOS (arm64) pass fail pass pass
Windows fail fail fail¹ pass

¹ Windows + uv 0.2.37 fails even lock-only. The rebuilt wheel (01663f71…) matches what Linux vendoring produces, but not the wheel Windows vendoring committed. That suggests the original scan --mode vendored on that cell was also built from the installed dist, and so isn't reproducible from the pristine wheel. It's likely the same root cause, but I haven't isolated it.

First bad

Not bisected. The published 4.0.0 predates the current uv vendoring backend (#238 / #239).

Suspect code

  • crates/socket-patch-core/src/vendor/pypi.rs:1824: the local build from the installed dist runs whenever a site-packages copy exists, with no check that the installed copy is the pristine registry artifact rather than the vendored wheel.
  • crates/socket-patch-cli/src/commands/repair_vendor.rs:1682: the vendor_artifact_rebuild_failed exit, which also drops the vendor dir.
  • Related but distinct: Vendored Poetry repair and vendor re-run can't rebuild a deleted wheel on a lock-only checkout because they fetch PyPI by the patched wheel's hash #380 (Poetry, lock-only: the lookup uses the patched hash).

Probe run

https://github.com/SocketDev/socket-patch/actions/runs/36776160780 (ubuntu / macos / windows × uv 0.2.37 and 0.12.21, lockOnly and withPatchedVenv cases)

Activity

  1. Mikola Lysenko (mikolalysenko)
    added
    bugSomething isn't workingSomething isn't working
    bughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agent
    pm:uvuvuv
    on Sep 30, 2026
  2. mikolalysenko commented on Sep 30, 2026

    @mikolalysenko
    Mikola Lysenko (mikolalysenko)
    on Sep 30, 2026
    CollaboratorAuthor
    More actions

    [agent] Triaged: priority:p1 (uv / PyPI family). Not a duplicate and no fix PR.

    The cause is that the vendored PyPI acquire path (vendor/pypi.rs:1824) builds from the installed dist whenever one exists. It doesn't check whether that dist is the pristine registry artifact or an install of Socket's own vendored wheel (direct_url.json pointing at .socket/vendor/...). This is related to #380 (Poetry lock-only), but the code path and the fix are different, so the two aren't clustered.


    Generated by Claude Code

  3. Mikola Lysenko (mikolalysenko)
    added
    agent:triaged
    priority:p1
    on Sep 30, 2026
  4. mikolalysenko commented on Oct 1, 2026

    @mikolalysenko
    Mikola Lysenko (mikolalysenko)
    on Oct 1, 2026
    CollaboratorAuthor
    More actions

    [agent] Re-triage: fixed on main 2463257 (#277, v5). Vendoring now commits the verified service artifact (vendor_prebuilt_downloaded) instead of building from the installed dist. repair re-fetches those exact bytes, so an installed patched copy no longer gets in the way.

    Same driver as the original report (vendored scan → commit → clone → delete the committed wheel → repair → fresh uv sync --locked, with and without a prior uv sync). The mock now also returns integrity.sha512, which v5 vendoring requires.

    OS uv 0.2.37 venv / lock-only uv 0.5.31 venv / lock-only uv 0.12.21 venv / lock-only
    Linux (sandbox, run 2×) pass / pass – pass / pass
    Windows (probe) pass / pass pass / pass pass / pass

    Every cell: repair exit 0 with rebuilt, the wheel is back, uv sync --locked passes and installs the patched six.py (c0c1c71c…). The Windows + 0.2.37 cell (footnote ¹ in the report) passes too. Probe: https://github.com/SocketDev/socket-patch/actions/runs/36806727354

    Closing as fixed.


    Generated by Claude Code

  5. Mikola Lysenko (mikolalysenko)
    closed this as completedon Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:uvuvuvpriority:p1

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.