Skip to content

Hosted Pipenv scan still pins an interpreter-bound cp311-none-any patched wheel into Pipfile.lock with no warning, so pipenv sync fails on every other Python version (gap in the #932 fix) #1048

Description

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

Summary

#984 (fixing #932 / #701) withholds platform- or ABI-tagged patched wheels from every hosted PyPI rewriter (redirect_pypi_platform_wheel). But the tag rule treats any <py>-none-any triple as portable, including an interpreter-bound python tag such as cp311-none-any. pip installs a cp311-none-any wheel only on CPython 3.11. So when the patch service grants a pure-Python patched wheel tagged cp311-none-any, hosted scan still narrows the cross-version Pipfile.lock entry to that one wheel, reports success with no warning, and pipenv sync then fails on Python 3.12, 3.13 and so on. That's the same breakage #932 describes, with exactly the rationale CLI_CONTRACT.md gives for the refusal ("installs on any other interpreter … would fail").

Impact

A project whose Pipfile.lock is shared across Python versions (CI matrix, dev machines vs. Docker images on different CPython minors) breaks on every interpreter except the one the wheel was built for. The scan exits 0 with status: success and no warning. The only signal is a hard install failure later (six-1.16.0-cp311-none-any.whl is not a supported wheel on this platform). This is loud rather than silent unpatching, but the lock that hosted mode wrote no longer installs.

The same rule (tag_is_platform_specific) drives vendored mode's vendor_platform_locked, so vendored very likely commits such a wheel too. I didn't verify vendored (my mock lacks the view files and blobs).

Repro (Linux, main 6fe81ad)

Patched wheel: the real six-1.16.0-py2.py3-none-any.whl with one line appended to six.py, with WHEEL retagged Tag: cp311-none-any and RECORD regenerated. The local mock API grants it at http://127.0.0.1:$PORT/patch/pypi/six/1.16.0/<token>/<uuid>/six-1.16.0-cp311-none-any.whl with its sha256. SOCKET_PATCH_SERVER_URL points at the mock.

printf '[[source]]\nurl = "https://pypi.org/simple"\nverify_ssl = true\nname = "pypi"\n\n[packages]\nsix = "==1.16.0"\n' > Pipfile
pipenv lock                                   # no python_version requirement
socket-patch scan --mode hosted --json        # exit 0, status success, redirect.warnings has no redirect_pypi_platform_wheel
jq .default.six Pipfile.lock                  # "file": ".../six-1.16.0-cp311-none-any.whl#sha256=903e…", hashes: [that one]
pipenv --python 3.11 sync && pipenv run python -c 'import six; print(six.SOCKET_PATCHED)'   # OK, PATCHED
pipenv --python 3.12 sync                     # exit 1: ERROR: six-1.16.0-cp311-none-any.whl is not a supported wheel on this platform.

pip's own tag check, using the same wheel bytes retagged:

wheel tag py3.11 py3.12 py3.13 socket-patch #984 rule
py2.py3-none-any ok ok ok portable (correct)
py311-none-any ok ok ok portable (correct: pyXY is accepted by later 3.x)
cp311-none-any ok refused refused portable (wrong)
py2-none-any refused refused refused portable (wrong, though unlikely for a py3 patch)
cp311-cp311-manylinux_2_17_x86_64 ok refused refused withheld, redirect_pypi_platform_wheel, lock untouched (correct)

Expected vs actual

  • Expected (CLI_CONTRACT.md, redirect_pypi_platform_wheel): the refusal exists because "a hosted pin would narrow the cross-platform lock entry to that one wheel, so installs on any other interpreter, OS or architecture would fail". A wheel whose python tag binds a single interpreter version (cpXY, or a pyX major that excludes the running major) has the same effect, so it should be withheld with the same warning. Alternatively it could be allowed only when the project provably pins that interpreter, e.g. Pipfile [requires] python_full_version / python_version = 3.11.
  • Actual: pinned, status: success, no warning, and pipenv sync on any other CPython minor fails.

OS × version

OS Pipenv sync py3.11 sync py3.12
Linux 2026.8.0 PATCHED fail (not a supported wheel)
Linux 2023.12.1 PATCHED fail (pipenv.exceptions.InstallError, not a supported wheel)
Linux 2026.8.0, control py2.py3-none-any PATCHED PATCHED
macOS / Windows untested; pip's tag rule is OS-independent for cp311-none-any, so the same failure follows on any non-3.11 interpreter

(Pipenv 2018.11.26 couldn't be run on py3.11 in the sandbox; that's a tooling artifact, not a data point.)

First bad

The refusal was introduced by #984 (23fd62e), and this gap has existed since then. Before #984, every platform wheel was pinned (#932).

Suspect code

  • crates/socket-patch-core/src/vendor/pypi_distribution.rs:113 tag_is_platform_specific: [_py, abi, plat] => *abi != "none" || *plat != "any" ignores the python tag. Its doc comment calls cp311-none-any "merely version-bound".
  • crates/socket-patch-core/src/patch/redirect/mod.rs:565 pypi_platform_wheel_refusal reuses it.
  • crates/socket-patch-core/src/patch/redirect/platform_wheel_tests.rs:237-250 pins cp311-none-any as redirectable.
  • crates/socket-patch-cli/CLI_CONTRACT.md:1313 documents the triple rule ("any tag triple other than <py>-none-any"), but its stated rationale covers this case.

No probe runs: this is pip's tag semantics, which don't depend on the OS.

Activity

  1. mikolalysenko commented on Oct 7, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: tag_is_platform_specific ignores the wheel's python tag, so interpreter-bound cpXY-none-any / py2-none-any wheels count as portable). Branch: agent/fix-pypi-interpreter-bound-wheel. Claim-ID: 2026-10-07T16:21:29Z-fa4a25


    Generated by Claude Code

  2. mikolalysenko commented on Oct 7, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1053


    Generated by Claude Code

  3. added 2 commits that reference this issue on Oct 7, 2026
    0a4c0a0
    0c7a92c
  4. mikolalysenko commented on Oct 7, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Pipenv bug-hunt (ledger #313): I tested the vendored variant on main 05ecc6e and checked PR #1053 (6bff12d) against real Pipenv.

    Vendored mode on main 05ecc6e: I ran scan --mode vendored against a grant serving six-1.16.0-cp311-none-any.whl on a lock-only Pipfile.lock project. It commits the wheel and wires default.six to ./.socket/vendor/pypi/<uuid>/six-1.16.0-cp311-none-any.whl. It gives no vendor_platform_locked advisory, and the status is success. I then took a fresh checkout (Pipfile + Pipfile.lock + .socket/), ran pipenv sync --python … and checked the installed six:

    Pipenv py3.11 py3.12
    2023.12.1 patched exit 1, six-1.16.0-cp311-none-any.whl is not a supported wheel on this platform.
    2026.8.0 patched exit 1, same error

    PR #1053 head 6bff12d, same projects, Pipenv 2023.12.1 / 2026.8.0:

    • Hosted: the redirect is withheld with "interpreter- or platform-specific (cp311-none-any)", and default.six stays the registry entry. The py3-none-any typing-extensions grant beside it is still pinned. This works as intended.
    • Vendored: the wheel is still committed, and the run now warns vendor_platform_locked ("Pipfile.lock now resolves it from this single-platform wheel only"). That matches how vendored mode already handles manylinux wheels (advisory, not refusal), so with the PR the vendored lane behaves as documented.

    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

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions