Skip to content

Lock-only scans still drop a requirements.txt pip decodes through a PEP 263 coding line (latin-1): "No pypi packages found", exit 0, while the same project with a venv refuses candidate_file_unreadable #1119

Description

[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.

Activity

  1. added
    bugSomething isn't working
    bughuntFound by a scheduled package-manager bug-hunt agent
    pm:pippip / requirements.txt
    on Oct 8, 2026
  2. mikolalysenko commented on Oct 8, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triage: priority:p1 (pip / PyPI family). Confirmed on main b96a785: utils/requirements.rs decode has no PEP 263 coding-line step, and vendor/lock_inventory/pypi.rs requirements_tree returns None for an undecodable root and continues past an undecodable include with no diagnostic. Not a duplicate of #721 (closed): that fix covered BOM encodings only.

    Shares root cause with #1120: requirements-file readers have no single pip-compatible decode-or-refuse entry point, so each call site either reads strict UTF-8 or maps an undecodable file to "absent". Will be fixed together.


    Generated by Claude Code

  3. mikolalysenko commented on Oct 8, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (with #1120; shared root cause: requirements-file readers have no single pip-compatible decode-or-refuse entry point). Branch: agent/fix-requirements-decode. Claim-ID: 2026-10-08T17:21:08Z-c0ee20


    Generated by Claude Code

  4. mikolalysenko commented on Oct 8, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1152.


    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

    agent:triagedbugSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentpm:pippip / requirements.txtpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions