[agent] Found by the scheduled pip / requirements.txt bug-hunt routine (ledger #309).
Summary
#724 (fix for #721) made lock-only discovery decode a requirements file by its BOM, the way pip does, and made hosted mode refuse a candidate file it can't decode (candidate_file_unreadable). pip's auto_decode has one more rule: with no BOM it honours a PEP 263 # -*- coding: <enc> -*- line in the first two lines. utils::requirements::decode returns None for such a file, and requirements_tree in lock-only discovery reads None as "no requirements file". So on a fresh checkout (no venv), scan prints No pypi packages found. and exits 0 with nothing pinned, and pip goes on to install the unpatched pin.
The same file in a project whose venv holds the package is refused loudly (candidate_file_unreadable, exit 1), so the refusal only fires when something else discovered the package first. A -r include in that encoding is dropped silently as well.
I raised this case in a comment on #721 before #724 merged (#721 (comment)). #721 was then closed with only the BOM cases fixed, so I'm filing the remainder here.
Impact
A CI job that runs socket-patch scan --mode hosted (or --mode vendored) on a fresh checkout before pip install -r requirements.txt reports success with nothing found. The install then pulls the unpatched release, and nothing in the output or the --json envelope says that a requirements file was skipped. The likeliest real-world trigger is a requirements file with a non-ASCII comment (a maintainer name, say) in a legacy encoding. pip accepts it whenever it carries a coding line.
Repro (Linux, main b96a785)
# Run in an empty directory, with VIRTUAL_ENV pointing at an EMPTY venv (fresh checkout / lock-only).
printf '# -*- coding: latin-1 -*-\n# Maintainer: Jos\xe9\nsix==1.16.0\n' > requirements.txt
pip install --no-deps --target t -r requirements.txt # pip 20.3.4 / 24.0 / 26.2.1: rc=0, installs six 1.16.0
# (without the coding line, pip fails: UnicodeDecodeError, so the coding line is what makes the file valid)
socket-patch scan --mode hosted --yes --ecosystems pypi
# -> "No pypi packages found." exit 0, requirements.txt unchanged
socket-patch scan --mode hosted --yes --json --ecosystems pypi
# -> {"status":"success","scannedPackages":0, ..., "redirect":{"redirected":0,"patches":[],"skipped":[],"warnings":[]}}
socket-patch scan --mode vendored --yes --json --ecosystems pypi
# -> status success, scannedPackages 0, exit 0
# Same file, but with a .venv holding six==1.16.0:
socket-patch scan --mode hosted --yes --json --ecosystems pypi
# -> exit 1, "requirements.txt is not UTF-8 text ... re-save it as UTF-8 and re-run; nothing was written"
# Include variant (lock-only): requirements.txt = "-r dev.txt", dev.txt = the latin-1 file above
# -> "No pypi packages found." exit 0; pip installs six from dev.txt.
socket-patch vex on the same lock-only checkout does warn cannot read requirements.txt: stream did not contain valid UTF-8, so the scan is the only silent path.
The patch API was a local mock serving a patch for pkg:pypi/six@1.16.0. Scans with a venv find and offer it.
Expected vs actual
OS × version
| Cell |
Lock-only hosted / vendored scan |
With venv (hosted) |
pip installs the file |
| Linux, pip 20.3.4 / py3.8 |
silent, exit 0 |
— |
yes |
| Linux, pip 24.0 / py3.12 |
silent, exit 0 |
— |
yes |
| Linux, pip 26.2.1 / py3.11 |
silent, exit 0 (×2, root and include) |
refuses candidate_file_unreadable, exit 1 |
yes |
| uv pip (control) |
— |
— |
no ("failed to decode file"), so pip only |
| macOS / Windows |
untested; the decode is OS-independent. On Windows, pip's no-BOM fallback is the locale codepage, so an ANSI (cp1252) file with no coding line is also valid there |
|
|
First bad: this never worked. Before #724 every non-UTF-8 file was skipped (#721); #724 fixed only the BOM encodings.
Suspect code
crates/socket-patch-core/src/utils/requirements.rs:26 (decode): BOMs, then String::from_utf8, with no PEP 263 coding-line step (pip: pip/_internal/utils/encoding.py::auto_decode).
crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs:733 (requirements_tree): read(ROOT)? turns an undecodable root into None (no requirements), and an undecodable include is continued, with no diagnostic in either case.
No probe runs: the sandbox can't delete probe branches, and the defect is in OS-independent decoding.
Backlog review — 2026-10-08
Priority: P1 → P2. Lock-only discovery misses a PEP 263 non-UTF8 requirements file. Retain support for the uncommon encoding; no false VEX evidence is claimed.
[agent] Found by the scheduled pip / requirements.txt bug-hunt routine (ledger #309).
Summary
#724 (fix for #721) made lock-only discovery decode a requirements file by its BOM, the way pip does, and made hosted mode refuse a candidate file it can't decode (
candidate_file_unreadable). pip'sauto_decodehas one more rule: with no BOM it honours a PEP 263# -*- coding: <enc> -*-line in the first two lines.utils::requirements::decodereturnsNonefor such a file, andrequirements_treein lock-only discovery readsNoneas "no requirements file". So on a fresh checkout (no venv),scanprintsNo pypi packages found.and exits 0 with nothing pinned, and pip goes on to install the unpatched pin.The same file in a project whose venv holds the package is refused loudly (
candidate_file_unreadable, exit 1), so the refusal only fires when something else discovered the package first. A-rinclude in that encoding is dropped silently as well.I raised this case in a comment on #721 before #724 merged (#721 (comment)). #721 was then closed with only the BOM cases fixed, so I'm filing the remainder here.
Impact
A CI job that runs
socket-patch scan --mode hosted(or--mode vendored) on a fresh checkout beforepip install -r requirements.txtreports success with nothing found. The install then pulls the unpatched release, and nothing in the output or the--jsonenvelope says that a requirements file was skipped. The likeliest real-world trigger is a requirements file with a non-ASCII comment (a maintainer name, say) in a legacy encoding. pip accepts it whenever it carries a coding line.Repro (Linux, main
b96a785)socket-patch vexon the same lock-only checkout does warncannot read requirements.txt: stream did not contain valid UTF-8, so the scan is the only silent path.The patch API was a local mock serving a patch for
pkg:pypi/six@1.16.0. Scans with a venv find and offer it.Expected vs actual
candidate_file_unreadableandvex'slockfile_unreadable. A silent success contradicts the reason Hosted and lock-only scans treat a UTF-16 requirements.txt (what Windows PowerShell'spip freeze >writes) as absent: exit 0, no warning, and pip keeps installing the unpatched pin #721 was fixed: a requirements file pip installs from must not be treated as absent.No pypi packages found., an emptyredirectblock and no warning, for both the root file and a-rinclude.OS × version
candidate_file_unreadable, exit 1First bad: this never worked. Before #724 every non-UTF-8 file was skipped (#721); #724 fixed only the BOM encodings.
Suspect code
crates/socket-patch-core/src/utils/requirements.rs:26(decode): BOMs, thenString::from_utf8, with no PEP 263 coding-line step (pip:pip/_internal/utils/encoding.py::auto_decode).crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs:733(requirements_tree):read(ROOT)?turns an undecodable root intoNone(no requirements), and an undecodable include iscontinued, with no diagnostic in either case.No probe runs: the sandbox can't delete probe branches, and the defect is in OS-independent decoding.
Backlog review — 2026-10-08
Priority: P1 → P2. Lock-only discovery misses a PEP 263 non-UTF8 requirements file. Retain support for the uncommon encoding; no false VEX evidence is claimed.