Repository navigation
PDM PEP 582 detection misses python.use_venv = false as PDM 2.27+ writes it (a TOML string) and in the user config, so agent mode patches an activated or stray venv and leaves __pypackages__ unpatched #609
Description
[agent] Found by the scheduled PDM bug-hunt routine (ledger #312).
Summary
#540 taught Python env discovery that a PDM project with a base interpreter in .pdm-python installs into __pypackages__/<X.Y>/lib (PEP 582) when python.use_venv is off. The setting is read in two ways that miss what PDM actually writes:
- PDM ≥ 2.27.0 writes the value as a TOML string.
pdm config -l python.use_venv falseproducesuse_venv = "false"inpdm.toml(2.26.9 and older write a barefalse).pdm_uses_venvreads it with.as_bool(), getsNone, and falls back to "on". PDM itself parses both forms asFalse. - The user config is never read. The default scope of
pdm config python.use_venv falseis the user config (~/.config/pdm/config.toml), which is how PDM's docs enable PEP 582. Discovery only looks atPDM_USE_VENVand the project'spdm.toml/.pdm.toml. This happens on every PDM version.
Either way discovery decides PDM uses a venv. It then takes the generic probes, so an activated VIRTUAL_ENV or a stray ./.venv (for example one an IDE or uv left behind) wins over __pypackages__. PDM ignores both, because the saved interpreter is a base Python and use_venv is off.
Impact
scan --mode agent / get patch a venv the project never runs and print status: success with exit 0. The copy that pdm run imports (__pypackages__) stays unpatched. vex does fail closed here (not_applied), so CI gated on VEX catches it. A plain scan --sync cron does not.
Repro (Linux, real PDM 2.29.2, mock patch API serving urllib3 1.26.18)
mkdir p && cd p
cat > pyproject.toml <<'EOF'
[project]
name = "p"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["urllib3==1.26.18"]
[tool.pdm]
distribution = false
EOF
pdm config -l python.use_venv false # writes use_venv = "false" on PDM >= 2.27
# or: pdm config python.use_venv false # user config, any PDM version
pdm use -f /usr/bin/python3.12 # "Using __pypackages__ because non-venv Python is used."
pdm install # -> __pypackages__/3.12/lib/urllib3
uv venv .venv && uv pip install -p .venv/bin/python urllib3==1.26.18 # stray venv PDM ignores
socket-patch scan --mode agent --json # exit 0, status success
tail -1 .venv/lib/python3.12/site-packages/urllib3/_version.py # patched
tail -1 __pypackages__/3.12/lib/urllib3/_version.py # still original
pdm run python -c 'import urllib3; print(urllib3.__file__)' # __pypackages__/3.12/lib/urllib3/__init__.pyActivating an unrelated venv (VIRTUAL_ENV=/some/other/venv) instead of the stray .venv gives the same result: that venv gets patched.
Expected vs actual
- Expected (docs/testing/pdm-compatibility.md, "The installed env is the one PDM records"): "A base interpreter means PEP 582 (
__pypackages__/<X.Y>/lib) only whenpython.use_venvis off (PDM_USE_VENV, or[python] use_venvinpdm.toml/.pdm.toml…) … Agent mode patches the env found". So withuse_venvoff, agent mode should patch__pypackages__. - Actual:
use_venv = "false"inpdm.toml(PDM ≥ 2.27's own output) anduse_venvin the user config are both treated as "on", and the activated or stray venv gets patched.
Matrix (Linux, main 045d7ec, each cell run twice)
| PDM | where use_venv=false is set |
value written | agent patches | result |
|---|---|---|---|---|
| 2.29.2 | project pdm.toml (pdm config -l) |
"false" (string) |
stray .venv / activated VIRTUAL_ENV |
fail |
| 2.29.2 | project pdm.toml, hand-written bool |
false |
__pypackages__ |
pass |
| 2.29.2 | user config (pdm config) |
"false" |
stray .venv |
fail |
| 2.26.9 | project pdm.toml (pdm config -l) |
false (bool) |
__pypackages__ |
pass |
| 2.26.9 | user config (pdm config) |
false |
stray .venv |
fail |
| 2.29.2 | PDM_USE_VENV=false env (control) |
— | __pypackages__ |
pass |
String-valued writes start at PDM 2.27.0: 2.12.4, 2.20.1, 2.24.2 and 2.26.9 write a bool, and 2.27.0, 2.28.2, 2.29.0 and 2.29.2 write a string. PDM 2.12.4 and 2.29.2 both read use_venv = "false" as False (pdm config python.use_venv). macOS and Windows weren't run. The logic is OS-independent, but the user-config path differs there (~/Library/Application Support/pdm/config.toml, %LOCALAPPDATA%\pdm\pdm\config.toml). The site config (/etc/xdg/pdm/config.toml, cf. #566) and PDM_CONFIG_FILE would be missed the same way.
First bad version
This is new code from #540 (b0a32db). Before that, PEP 582 wasn't crawled at all (documented), so it's a gap in the fix rather than a regression from a release.
Suspect code
crates/socket-patch-core/src/crawlers/python_crawler.rs:493-510(pdm_uses_venv):.as_bool()drops string values, so it should parse them the way PDM'sensure_booleandoes, likepdm_env_flag. It also never consults the user or site config (PDM_CONFIG_FILE,<user config dir>/pdm/config.toml), whichpdm_global_site_packages(:1987) already locates.:479-487is the caller that returnsNone→ generic probes (VIRTUAL_ENVat:367-384,./.venvat:405-410) win over__pypackages__.
Activity
mikolalysenko commentedon Oct 2, 2026
[agent] Triage: priority:p1 (PDM). Confirmed on main
045d7ec:pdm_uses_venv(python_crawler.rs~L493-510) reads[python] use_venvwith.as_bool(), so it drops PDM 2.27+'s string form. It also only looks at the project'spdm.toml/.pdm.toml, never the user config,$PDM_CONFIG_FILEor the site config.Shares root cause with #566: PDM settings are read from one file at a time, not through PDM's layered config (env > project >
$PDM_CONFIG_FILE/user > site, withensure_boolean-style coercion). Will be fixed together.Generated by Claude Code
mikolalysenko commentedon Oct 2, 2026
[agent] Claiming this issue (with #566; shared root cause: PDM settings aren't resolved through PDM's layered config). Branch: agent/fix-pdm-layered-config. Claim-ID: 2026-10-02T21:20:53Z-8748ca
Generated by Claude Code
mikolalysenko commentedon Oct 2, 2026
[agent] Draft PR: #611
Generated by Claude Code
Read PDM settings through PDM's config layers
Fix PDM settings ignoring PDM's config layers (#609, #566) (#611)