Repository navigation
Poetry source-pin guard lets hosted and vendored rewrite a lock-version 1.1 poetry.lock that no Poetry 2.x can install, and the advisory says 2.3+ install it #1503
Description
[agent] Found by the scheduled Poetry bug-hunt routine (ledger #311).
Summary
The #1282 / #1447 guard decides whether to refuse a source = "pypi"-pinned dependency by the lock's writer (source_constraint_breaks_writer: refuse for 1.3 – 2.2). A poetry.lock with lock-version = "1.1" (written by Poetry 1.1 or 1.2) therefore passes, and both modes rewrite it with only the redirect_poetry_source_constraint / pypi_poetry_source_constraint advisory, which says:
Poetry 1.3 – 2.2 refuse to install poetry.lock with that constraint (1.1 – 1.2 and 2.3+ install it)
That's wrong for this lock format. Every Poetry 2.x installer I tried (2.0.1, 2.2.1, 2.3.4, 2.4.3, 2.5.1) fails on the rewritten lock-1.1 file with Repository "pypi" does not exist. and installs nothing. Only Poetry 1.1 / 1.2 install it. The same unpatched lock installs fine on all of them.
Root cause on Poetry's side: Locker.is_locked_groups_and_markers() returns true only for lock-version >= 2.1. For an older lock, Poetry 2.x re-runs the solver against the locked repository, and that solve hits the source = "pypi" constraint exactly as 1.3 – 2.2 do. The 2.3+ "installs it" measurement in #1282 only holds for lock 2.1 files (written by 2.x itself), where Poetry 2.3+ skips the re-solve.
Impact
A project that still carries a Poetry 1.1 / 1.2 lock and installs with a current Poetry (common: old lock, Poetry upgraded in CI, lock never re-generated) gets a lock that breaks poetry install, on a fresh checkout or in CI. The scan exits 0 with status: success, and the advisory tells a user on Poetry 2.3+ they're fine. This is the #1282 failure again, on the lock format the version window can't see.
Repro (Linux, Python 3.11)
Patch data comes from a local mock of the patch API serving a patched six-1.16.0 wheel (same routes as crates/socket-patch-cli/tests/e2e_vex_build/poetry.rs).
mkdir proj && cd proj
cat > pyproject.toml <<'EOF'
[tool.poetry]
name = "demo"
version = "0.1.0"
description = ""
authors = ["x <x@x.x>"]
[tool.poetry.dependencies]
python = ">=3.8"
six = {version = "1.16.0", source = "pypi"}
EOF
poetry-1.2.2 lock # lock-version = "1.1", no @generated stamp (1.1.15 gives the same)
socket-patch scan --mode hosted --yes --json $API # exit 0, success; warnings redirect_poetry_source_constraint ("... 2.3+ install it"), redirect_poetry_stale_install_risk
mkdir ../fresh && cp pyproject.toml poetry.lock ../fresh && cd ../fresh
POETRY_VIRTUALENVS_IN_PROJECT=true poetry-2.5.1 install --no-root
# Repository "pypi" does not exist. (six not installed)scan --mode vendored (copying .socket/ too) behaves the same: exit 0, lock rewired to type = "file", then the same error under any 2.x installer.
Control: the unpatched lock-1.1 file installs on 1.3.2, 1.8.5 and 2.5.1.
Expected vs actual
- Expected: docs/testing/poetry-compatibility.md (lines 38–51) says both modes refuse the package before any write when the installer can't consume the result, and otherwise warn accurately about which installers refuse it. A lock-1.1 file with a source-pinned dependency is refused by every 1.3+ installer, so it should either be refused like a 1.3 – 2.2 writer's lock or carry an advisory that says only Poetry 1.1 – 1.2 install it.
- Actual: rewritten with exit 0, and the advisory (and docs line 43) claim 2.3+ install it.
Writer × installer (Linux, main aa85c6c, hosted unless noted)
| Writer (lock format) | Installer 1.1.15 | 1.2.2 | 1.3.2 | 2.0.1 | 2.2.1 | 2.3.4 | 2.4.3 | 2.5.1 |
|---|---|---|---|---|---|---|---|---|
1.1.15 (1.1) |
pass (patched) | pass | – | fail | fail | fail | fail | fail |
1.2.2 (1.1) |
pass (patched) | pass | fail (solver) | fail | fail | fail | fail | fail |
| 1.1.15 / 1.2.2, vendored | – | – | fail | – | – | fail | – | fail |
2.3.4 (2.1) |
– | can't read lock | – | – | fail (documented) | pass | – | pass |
2.5.1 (2.1) |
– | can't read lock | – | – | fail (documented) | pass | – | pass |
1.3.2 (2.0) |
refused before write (#1447 fix works) |
Each failing cell is Repository "pypi" does not exist. (1.3.2: version solving failed), with six not installed. Reproduced on two separate projects (1.1.15 and 1.2.2 writers), both modes. Poetry 1.0.10 can't lock a source = "pypi" dependency at all, so lock format 1.0 is n/a. I didn't run macOS or Windows (probe branches are blocked for this routine). The guard is a text check on the lock, with nothing OS-specific.
First bad commit
Not a regression. The writer-keyed window came in with #1413 (fix for #1282) and was extended to 1.3 by #1451 (#1447). Both measured 2.3+ only with locks that 2.3+ wrote.
Suspect code
crates/socket-patch-core/src/utils/poetry_lock.rs:370source_constraint_breaks_writer:lock_writer_versionreturnsNonefor lock 1.0 / 1.1, so the guard never fires for them. The condition that actually decides whether Poetry 2.x re-solves islock-version < 2.1.crates/socket-patch-core/src/utils/poetry_lock.rs:343source_constraint_advisoryanddocs/testing/poetry-compatibility.md:43: "1.1 – 1.2 and 2.3+ install it".- Poetry 2.5.1
poetry/packages/locker.py:126is_locked_groups_and_markers(lock-version >= 2.1).
Activity
mikolalysenko commentedon Oct 13, 2026
[agent] Claiming this issue (shared root cause: the Poetry source-pin guard keys on the lock's writer version, so a lock-version 1.0/1.1 file, which every Poetry 1.3+ re-solves, is never refused and gets a wrong advisory). Branch: agent/fix-poetry-old-lock-source-pin. Claim-ID: 2026-10-13T21:21:17Z-2e3678
Generated by Claude Code
mikolalysenko commentedon Oct 13, 2026
[agent] Draft PR: #1506
Generated by Claude Code
Test Poetry source pin over a pre-2.1 lock
Refuse Poetry source pins over pre-2.1 locks
Narrow the old-lock source-pin refusal
Keep the 1.3 - 2.2 range in the source advisory
poetry: run 21 ledger (#1527, #1503 verified fixed)