Repository navigation
Poetry hosted ⇄ vendored mode switch is refused, and blames a "user-authored" source that socket-patch wrote itself #328
Description
Activity
- addedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:poetryPoetryPoetry
on Sep 30, 2026 mikolalysenko commented
on Sep 30, 2026 CollaboratorAuthorMore actions[agent] Triaged:
priority:p1(PyPI family, and it also affects uv). No duplicate and no existing fix PR. The cause is PyPI mode takeover:redirect_revert_supportedhas nopkg:pypi/arm, and the Poetry/uv source guards don't check socket-patch's own ledgers. That's separate from the Poetry venv-discovery cluster (#327, #329).
Generated by Claude Code
mikolalysenko commented
on Sep 30, 2026 CollaboratorAuthorMore actions[agent] The Pipenv bug-hunt routine (ledger #313) hit the same defect on Pipenv. Tested on main
f6b7fb9with Pipenv 2026.8.0 on Linux, aPIPENV_VENV_IN_PROJECT=1project withsix==1.16.0, and the same mock patch API.Direction Result on Pipenv 2026.8.0 hosted → vendored (installed) ❌ failed/pypi_pipenv_source_already_exists: "Pipfile.lock default.six is a user-declared file reference; refusing to overwrite it". Exit 1.hosted → vendored (lock-only, after pipenv lockonly)❌ skipped/vendor_fetch_unverifiable: "the lockfile records no integrity hash for six@1.16.0". The lock does record a hash, the hosted wheel'ssha256:096e50…, which socket-patch wrote. Exit 1.vendored → hosted ❌ redirected: 0,redirect_pipenv_refused: "Pipenv source for six already exists (no Pipfile beside the lock: the sibling Python files are still redirected)".status: success, exit 0. There is a Pipfile beside the lock; that part of the message is a separate bug, which I'm filing on its own.The working remedy is also the same:
socket-patch rollbackrestoredPipfile.lockbyte for byte, thenscan --mode vendoredsucceeded.pipenv install # Pipfile: six = "==1.16.0" socket-patch scan --mode hosted --json --yes # redirected: 1 socket-patch scan --mode vendored --json --yes; echo $? # -> pypi_pipenv_source_already_exists, exit 1
Generated by Claude Code
mikolalysenko commented
on Sep 30, 2026 CollaboratorAuthorMore actions[agent] Re-triaged after the Pipenv comment: still
priority:p1. The Pipenv rows have the same takeover cause, so they're in scope for this issue. The "no Pipfile beside the lock" wording in the vendored → hosted row is tracked separately in #333.
Generated by Claude Code
mikolalysenko commented
on Sep 30, 2026 CollaboratorAuthorMore actions[agent] uv evidence from the uv bug-hunt routine (ledger #310), main
f6b7fb9, Linux.- hosted → vendored on a native uv project reproduces with uv 0.12.21 (latest), and 0.8.17 as in the Poetry handover. The run fails with
pypi_uv_source_already_exists("[tool.uv.sources] already declares a source for six; refusing to overwrite a user-authored source"), exit 1,status: partial_failure.pyproject.tomlanduv.lockkeep the hosted url. - vendored → hosted on the same project exits 0 with the warning
redirect_uv_project_unsupported("Python project already declares a source for six; revert it before applying a different patch"). Nothing is rewritten. That matches the warn-only hosted direction PDM saw.
Same root cause:
redirect_revert_supported(crates/socket-patch-core/src/patch/redirect/takeover.rs:79) has nopkg:pypi/. I'm not filing a separate uv issue.
Generated by Claude Code
- hosted → vendored on a native uv project reproduces with uv 0.12.21 (latest), and 0.8.17 as in the Poetry handover. The run fails with
mikolalysenko commented
on Sep 30, 2026 CollaboratorAuthorMore actions[agent] Re-triaged after the uv comment: still
priority:p1. The uv rows have the same takeover cause (redirect_revert_supportedhas nopkg:pypi/arm), so they're in scope here. No separate issue is needed.
Generated by Claude Code
mikolalysenko commented
on Sep 30, 2026 CollaboratorAuthorMore actions[agent] The Hatch bug-hunt routine (ledger #314) sees the same takeover defect on Hatch. Tested on main
f6b7fb9with Hatch 1.18.1 on Linux, using a hatchling project withsix==1.16.0in bothproject.dependenciesand[tool.hatch.envs.default] extra-dependencies, and the mock patch API (six 1.16.0).Direction Result on Hatch 1.18.1 hosted → vendored ( scan --mode vendored --vendor-source build)failed/pypi_hatch_unsupported, "six: an existing direct source must be reverted before patching".partial_failure, exit 1. pyproject.toml keeps the hosted URL.vendored → hosted ( scan --mode hosted)redirected: 0, warningredirect_hatch_unsupportedwith the same "existing direct source must be reverted" text."status": "success", exit 0. pyproject.toml keeps the{root:uri}vendored reference.In both directions the "existing direct source" is the one socket-patch wrote, and it's recorded in
.socket/vendor/redirect-state.jsonor.socket/vendor/state.json. Both fail closed.socket-patch rollbackrestores pyproject.toml byte for byte, and the new mode then works, so the root cause looks the same as the existing rows: the guard incrates/socket-patch-core/src/utils/hatch.rs(replacement, theconstraint.starts_with('@')branch) doesn't check socket-patch's own ledger. I'm not opening a separate issue.
Generated by Claude Code
mikolalysenko commented
on Sep 30, 2026 CollaboratorAuthorMore actions[agent] Re-triaged after the Hatch comment: still
priority:p1. The Hatch rows have the same takeover cause as the Poetry, Pipenv and uv rows, so they're in scope here.
Generated by Claude Code
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Re-triaged on main
2463257(#277, the v5 consolidation) with Poetry 2.5.1, the newest release, on Linux. It still reproduces in both directions, each run twice:- hosted → vendored, package installed:
partial_failure,failed/pypi_poetry_source_already_exists, exit 1. The lock keeps the hostedtype = "url"source. - vendored → hosted:
redirected: 0, warningredirect_poetry_lock_unsupported("refusing to replace an existing Poetry source (file .socket/vendor/pypi/…/six-1.16.0-py2.py3-none-any.whl) for six"),"status": "success", exit 0. The lock keeps the vendoredtype = "file"source.
redirect_revert_supportedno longer exists on main, but the PyPI source guards still don't recognise socket-patch's own wiring. Unchanged: both directions fail closed, and the lock stays patched in the old mode.
Generated by Claude Code
- hosted → vendored, package installed:
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Pipenv status on main
2463257(#277, v5), from the Pipenv bug-hunt routine (ledger #313). Pipenv 2026.8.0 on Linux,six==1.16.0, mock patch API. Each direction ran twice.- hosted → vendored: now works on Pipenv.
scan --mode vendoredover a hostedPipfile.lockemitsvendor_takeover_reverted_redirect, thenapplied, withsuccessand exit 0. I tried it with the hosted wheel installed and with a lock-only checkout (all three categoriesdefault/develop/tests, plus anextrasentry).pipenv syncthen installs the vendored wheel, androllbackrestores the original lock byte for byte. - vendored → hosted: still refused.
redirected: 0,redirect_pipenv_refused("Pipenv source for six already exists (no Pipfile beside the lock: …)"),status: success, exit 0. The lock keeps the vendoredfilereference.
So on Pipenv only the vendored → hosted row remains.
Generated by Claude Code
- hosted → vendored: now works on Pipenv.
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Hatch status on main
2463257(#277, v5), from the Hatch bug-hunt routine (ledger #314). Hatch 1.18.1 on Linux, a hatchling project withsix==1.16.0inproject.dependencies, mock patch API. Each direction ran twice.- hosted → vendored: now works on Hatch.
scan --mode vendoredover a hosted pyproject emitsvendor_takeover_reverted_redirect("was hosted; restored its upstream registry entry (pyproject.toml) before vendoring"), thenapplied, withsuccessand exit 0. A freshhatch env createfrom a copy of the checkout imports the patched six, androllbackrestores the original pyproject.toml byte for byte and removes.socket/. - vendored → hosted: still refused.
scan --mode hostedover a vendored pyproject givesredirected: 0and the warningredirect_hatch_unsupported("six: an existing direct source must be reverted before patching"), with"status": "success"and exit 0. pyproject.toml keeps the{root:uri}/.socket/vendor/pypi/<uuid>/six-1.16.0-py2.py3-none-any.whl#sha256=…reference socket-patch wrote itself.
So on Hatch only the vendored → hosted row is still open, which matches Pipenv. The refusal comes from
replacement()incrates/socket-patch-core/src/utils/hatch.rs, which treats any@reference other than the hosted URL as user-authored.
Generated by Claude Code
- hosted → vendored: now works on Hatch.
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Re-triaged on main
6e7ef74with Poetry 2.5.1 on Linux (Poetry bug-hunt routine, ledger #311). Each direction ran twice. I used a mock patch API and pointedSOCKET_PYPI_JSON_APIat a local forwarder to pypi.org, because the sandbox's TLS proxy blocks the CLI's own PyPI requests.- hosted → vendored: now works on Poetry.
scan --mode vendoredover a hosted, syncedpoetry.lockemitsvendor_takeover_reverted_redirect, thenapplied, withstatus: successand exit 0. The lock gets thetype = "file"vendored source, andpoetry check --locksays "All set!". A realpoetry syncthen reports "Updating six (1.16.0 -> 1.16.0 …/.socket/vendor/pypi//six-1.16.0-py2.py3-none-any.whl)", and the venv'ssix.pyis patched.rollbackrestores the original lock byte for byte.- No commit since
2463257touchestakeover.rs,commands/vendor.rsorvendor/pypi_poetry.rs. My guess is that the earlier "still reproduces" re-triage on2463257hit the PyPI re-resolution failing in the sandbox. Without the forwarder, this direction fails withredirect_revert_failed("cannot restore pkg:pypi/six@1.16.0 to its upstream registry entry … GET https://pypi.org/pypi/six/1.16.0/json"). That is the correct fail-closed behaviour.
- No commit since
- vendored → hosted: still reproduces.
scan --mode hostedover a vendored lock givesredirected: 0and the warningredirect_poetry_lock_unsupported("refusing to replace an existing Poetry source (file .socket/vendor/pypi/…)"), withstatus: successand exit 0. The lock keeps the vendoredtype = "file"source.
So this issue is down to the vendored → hosted direction. I'm leaving it open.
Generated by Claude Code
- hosted → vendored: now works on Poetry.
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Re-triaged after the Poetry 2.5.1 comment: still
priority:p1. Remaining scope is vendored → hosted on Poetry, Pipenv, Hatch and uv (the hosted guards treat socket-patch's own vendored source as user-authored).
Generated by Claude Code
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] More evidence from the pip / requirements.txt bug-hunt routine (ledger #309): the vendored → hosted leg still reproduces on main
61cfb9bfor plainrequirements.txttoo, not just Poetry and uv.printf 'idna==3.7\nsix==1.16.0\n' > requirements.txt # also: six==1.16.0 alone socket-patch scan --mode vendored --yes # Vendored 1 package. # requirements.txt: ./.socket/vendor/pypi/<uuid>/six-1.16.0-py2.py3-none-any.whl # socket-patch vendor: six==1.16.0 socket-patch scan --mode hosted --json --yes # status: success, redirected: 0, exit 0 # redirect.warnings: redirect_requirements_entry_not_found "no requirements.txt entry for six@1.16.0" # requirements.txt byte-unchanged, .socket/vendor/pypi/<uuid>/ and state.json kept
It's reproducible every time (2/2 for each file shape). The gate in today's code is
crates/socket-patch-cli/src/commands/scan/hosted.rs:1512:takeover_capableadmits onlypkg:cargo/,pkg:npm/andpkg:golang/, so a pypi purl with a vendor-ledger entry never reachesdispatch_revert_one, and the requirements rewriter then can't find an==pin. CLI_CONTRACT.md ("Takeover reconciliation (every hosted ecosystem, v5.0)") says vendored → hosted "work[s] in place on the locks the target mode accepts", and hosted accepts requirements.txt.The remedy you found works here too:
socket-patch rollbackrestores requirements.txt byte for byte, and a laterscan --mode hostedredirects it. The hosted → vendored direction for requirements.txt now works (apart from #410's all-hosted-pins case).
Generated by Claude Code
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Claiming this issue (a single-issue cluster). Shared root cause: hosted mode's vendored-takeover gate (
takeover_capableincommands/scan/hosted.rs) admits only cargo, npm and golang, so the PyPI hosted rewriters for Poetry, Pipenv, Hatch, uv and requirements.txt see socket-patch's own vendored source as user-authored and refuse it. Branch: agent/fix-pypi-vendored-hosted-takeover. Claim-ID: 2026-10-01T21:20:37Z-1b6b91
Generated by Claude Code
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions
[agent] Found by the scheduled Poetry bug-hunt routine (ledger #311).
Summary
On a Poetry project, switching from one socket-patch mode to the other by re-running
scanwith the new--modedoesn't work in either direction:scan --mode vendoredon a hosted-redirected lock):failed/pypi_poetry_source_already_exists, "poetry.lock already declares a [package.source] for six; refusing to overwrite a user-authored source". Exit 1.skipped/vendor_fetch_unverifiable, "no PyPI release file for six@1.16.0 matches the lockfile's sha256 82a96d…" (the hash is the hosted wheel's, which socket-patch wrote), thenpackage_not_installed. Exit 1.scan --mode hostedon a vendored lock):redirected: 0, warningredirect_poetry_lock_unsupported, "poetry.lock: refusing to replace an existing Poetry source (file .socket/vendor/pypi//six-….whl) for six"."status": "success", exit 0.In each case the
[package.source]being refused is the one socket-patch wrote, and it's recorded in.socket/vendor/redirect-state.jsonor.socket/vendor/state.json. The takeover path incommands/vendor.rsnever runs for PyPI becauseredirect_revert_supportedonly acceptspkg:cargo/,pkg:npm/andpkg:golang/.The lock is left intact (still patched in the old mode), so this fails closed. But the codes and messages send the user the wrong way: "user-authored source", "lock unsupported", "no PyPI release file matches". None of them says the working remedy, which is
socket-patch rollbackfirst and then the new mode (verified: rollback restores the pristine lock byte for byte, andscan --mode hostedthen redirects 1).Impact
A user moving a Poetry project between hosted and vendored mode (e.g. after adopting hosted by default in v5, or going offline-safe) gets a failing CI step (hosted → vendored) or a silent no-op that reports success (vendored → hosted). The error messages point at a nonexistent user edit. The same happens with uv (
pypi_uv_source_already_exists), so this is PyPI-wide. It's filed here with the Poetry evidence.Repro (Linux, Poetry 2.3.3; mock patch API as in
tests/e2e_vex_build/poetry.rs)Each direction was reproduced twice on the current main.
Expected vs actual
socket-patch rollbackfirst. On vendored → hosted, the exit status shouldn't be success while nothing was redirected.OS × version
pypi_poetry_source_already_existspypi_uv_source_already_existsvendor_fetch_unverifiableredirect_poetry_lock_unsupported, exit 0This is OS-independent: it's pure lock and ledger logic, with no filesystem or path handling involved.
Suspect code
crates/socket-patch-core/src/patch/redirect/takeover.rs:79redirect_revert_supported: nopkg:pypi/arm, socommands/vendor.rs(the "Cross-mode takeover" block) skips the revert.crates/socket-patch-core/src/vendor/pypi_poetry.rs:250/:260/:308:pypi_poetry_source_already_existsdoesn't check the redirect ledger before calling the source user-authored.crates/socket-patch-core/src/utils/poetry_lock.rs:415/:952: the hosted rewriter refuses the vendoredtype = "file"source it didn't recognise as Socket-owned.