Skip to content

Hosted pnpm scan skips the trustLockfile: true auto-config when pnpm-lock.yaml starts with a UTF-8 BOM, so pnpm 11/12 frozen installs fail with ERR_PNPM_TARBALL_URL_MISMATCH after a successful scan #903

Description

[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).

Summary

On a pnpm-lock.yaml whose first line carries a UTF-8 BOM (\xEF\xBB\xBFlockfileVersion: '9.0'), scan --mode hosted rewrites the lock correctly (the hosted tarball and sha512 land) and reports success. But it does not write trustLockfile: true to pnpm-workspace.yaml. The version sniff formats::pnpm::lock_versions matches line.strip_prefix("lockfileVersion:"), and the BOM makes the first line miss, so lock_version_major returns None. The hosted engine's root-lock gate (root_lock_v9) then treats the lock as "not trust-policy era" and falls back to the manual guidance, as it would under --no-trust-lockfile-config.

pnpm itself reads BOM locks without complaint: a frozen install of the unmodified BOM lock succeeds on 11.28.3 and 12.8.1. A BOM typically comes from a Windows editor saving the lock "UTF-8 with signature".

Impact

  • pnpm 11 / 12: the next pnpm install --frozen-lockfile (CI) fails with ERR_PNPM_TARBALL_URL_MISMATCH, after a scan that exited 0 with status: success. The only signal is the generic redirect_pnpm_trust_lockfile text that also covers the opt-out case. The tempting pnpm-suggested fix (re-lock) discards the patch.
  • Legacy side of the same sniff: a BOM-prefixed pnpm 8 ('6.0') lock is no longer recognised as legacy (all_locks_legacy is false), so the warning gives the pnpm ≥ 11 advice (pnpm install --trust-lockfile, an option pnpm 8 doesn't have) instead of the legacy "no trust step needed" text. The pnpm 8 install itself works.

Repro (Linux, main 9c43dfc, local mock of the patch API)

mkdir bom && cd bom
echo '{"name":"b","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > package.json
pnpm install                                   # pnpm 12.8.1
printf '\xef\xbb\xbf' | cat - pnpm-lock.yaml > x && mv x pnpm-lock.yaml
rm -rf node_modules && pnpm install --frozen-lockfile --store-dir "$(mktemp -d)"   # OK: pnpm accepts the BOM lock
socket-patch scan --mode hosted --yes --json   # status success, rewrittenFiles: ["pnpm-lock.yaml"] only
ls pnpm-workspace.yaml                         # absent (without the BOM it is created with trustLockfile: true)
rm -rf node_modules && pnpm install --frozen-lockfile --store-dir "$(mktemp -d)"
# Error: ERR_PNPM_TARBALL_URL_MISMATCH
printf 'trustLockfile: true\n' > pnpm-workspace.yaml
rm -rf node_modules && pnpm install --frozen-lockfile --store-dir "$(mktemp -d)"   # OK, patched bytes installed

Expected vs actual

  • Expected (CLI_CONTRACT, "pnpm trust-config" paragraph): "For a 9.0 root lock, the CLI ensures pnpm-workspace.yaml carries trustLockfile: true". The rewriter already accepts this lock as 9.0 and splices it, so the trust gate should read the same version. A BOM-prefixed legacy lock should get the legacy text.
  • Actual: the trust step is skipped silently (exit 0), and the frozen install fails on pnpm ≥ 11.

Matrix (each cell run twice in fresh projects; Linux only)

pnpm lock trust written frozen install after scan
12.8.1 9.0 + BOM no fail ERR_PNPM_TARBALL_URL_MISMATCH
11.28.3 9.0 + BOM no fail ERR_PNPM_TARBALL_URL_MISMATCH
12.8.1 9.0, no BOM (control) yes pass
8.15.9 6.0 + BOM n/a pass, but the warning gives pnpm 11 advice

Release 4.0.0 behaves the same (no trust write on a BOM lock), so this isn't a regression. The code path is OS-independent; a Windows core.autocrlf + BOM checkout is the likely real-world source, but I didn't run a Windows probe.

Suspect code

  • crates/socket-patch-core/src/formats/pnpm/mod.rs:281 (lock_versions): line.strip_prefix("lockfileVersion:") without stripping a leading \u{FEFF} from the first line. lock_version_major (:297) and may_need_store_flag (:305, the shrinkwrapVersion: check) share the sniff.
  • crates/socket-patch-core/src/hosted/engine.rs:1294 (root_lock_v9) and :1307 (all_locks_legacy) consume it.

Probe runs: none (Linux reproduction only).

Activity

  1. mikolalysenko commented on Oct 6, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] More impact from the same root cause, found in the same run (main 9c43dfc, Linux).

    After the hosted scan has pinned a BOM-prefixed pnpm-lock.yaml, nothing can unwind or inspect the pin. The readers use a second BOM-blind sniff, formats/pnpm/grammar.rs:81 (is_pnpm_lock_text: line.starts_with("lockfileVersion:")), so discovery reports lockfile_unparseable for the very lock the scan just wrote:

    command (after scan --mode hosted on the BOM lock) result
    rollback --yes --json status: error, hosted_wiring_contested … (lockfile_unparseable: pnpm-lock.yaml is not a pnpm lockfile (no lockfileVersion / shrinkwrapVersion)). The lock stays pinned.
    remove pkg:npm/left-pad@1.3.0 --yes --json status: error. The lock stays pinned.
    list --json status: error, same hosted_wiring_contested
    vex "no hosted or vendored patch references were found … nothing to attest" (honest, but the patch is invisible)

    This reproduced on 4 projects (pnpm 12.8.1 ×2, 11.28.3 ×2). The error's own remedy, re-running scan --mode hosted, doesn't help. Only git checkout -- pnpm-lock.yaml or removing the BOM does. So the scan accepts a lock that every other command then refuses. Either both sides should strip a leading \u{FEFF}, or the hosted rewriter should refuse a BOM lock up front.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 6, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Structural cause tracked in #905. formats::pnpm has three lockfileVersion readers, and none of them skips a BOM, while yarn's sniff and about 50 other readers do. The same BOM lock is also refused by the vendored router (vendor_lockfile_version_unsupported) and reported as "not a pnpm lockfile" by VEX discovery. #905 adds one shared strip_bom, which makes this a one-line fix.


    Generated by Claude Code

  3. mikolalysenko commented on Oct 6, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Shares root cause with #904, #905: the formats::pnpm lock and workspace readers (head_lock_version, lock_versions, is_pnpm_lock_text, workspace::top_level_key) match a column-0 literal and never skip a leading UTF-8 BOM. Will be fixed together.


    Generated by Claude Code

  4. mikolalysenko commented on Oct 6, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (with #904, #905; shared root cause: pnpm lock/workspace readers don't skip a leading UTF-8 BOM). Branch: agent/fix-pnpm-bom-readers. Claim-ID: 2026-10-06T01:20:29Z-3fcf3e


    Generated by Claude Code

  5. mikolalysenko commented on Oct 6, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Fix in progress: #909


    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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions