Skip to content

Bun 1.3+ bun.lockb root is read as pre-1.3, so a "!"-excluded member can't be patched in hosted or vendored mode (regression in 5.0.1) #1492

Closed

Description

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

Summary

The #1097 fix (#1480, 1b5d8d7, first shipped in 5.0.1) picks how to read workspaces globs from the root lock. Any root that holds a binary bun.lockb and no bun.lock is read with the pre-1.3 Bun rules, where ! patterns are ignored (crates/socket-patch-core/src/hosted/governing_root.rs:576). But Bun 1.2 and later still write bun.lockb when bunfig.toml sets [install] saveTextLockfile = false, and Bun 1.3+ does apply ! patterns whichever lock format it writes.

So in a Bun 1.3.x or 1.4.x bun.lockb workspace with "workspaces": ["packages/*", "!packages/b"], packages/b is a separate project with its own bun.lockb, and Bun installs it from that lock. socket-patch still treats b as a workspace member:

  • scan --mode hosted run in packages/b refuses with redirect_workspace_lockfile_elsewhere. Its message is wrong: "Bun installs it from /bun.lockb and ignores its own ./bun.lockb". Exit 1.
  • scan --mode vendored run in packages/b fails vendor_lockfile_missing with the same message.
  • The remedy the message gives, running from the root, can't work either: the root lock doesn't contain b's packages. A hosted run there exits 0 (status: success) and skips the package with redirect_unconfirmed. A vendored run fails with vendor_lock_entry_not_found.

As a result, packages that only an excluded member depends on can't be patched in hosted or vendored mode. Release 5.0.0 pinned b's bun.lockb correctly, and a fresh frozen install of that lock installs the patched bytes.

The binary lock does show which Bun wrote it. The bun.lockb files written by Bun 1.3.9 and 1.4.3 both carry a cNfGvRsN trailer tag, which is the binary twin of the text lock's top-level configVersion. The file written by 1.2.23 has no such tag. The text-lock sniff at governing_root.rs:563-571 already handles the text lock correctly.

Impact

Every Bun ≥ 1.3 project that uses a binary lock and excludes a member with ! can no longer hosted- or vendored-patch that member. The refusal message wrongly says Bun ignores the member's own lock. The advised root run reports success with nothing pinned.

Repro (Linux, Bun 1.4.3, main 49847ef)

mkdir -p ws/packages/a ws/packages/b && cd ws && git init -q
printf 'node_modules/\n' > .gitignore
printf '[install]\nsaveTextLockfile = false\n' > bunfig.toml; cp bunfig.toml packages/b/
echo '{"name":"root","private":true,"workspaces":["packages/*","!packages/b"]}' > package.json
echo '{"name":"a","version":"1.0.0","dependencies":{"is-number":"7.0.0"}}' > packages/a/package.json
echo '{"name":"b","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > packages/b/package.json
bun install && (cd packages/b && bun install)   # b gets its own bun.lockb + node_modules/left-pad
bun ./bun.lockb | grep left-pad                  # nothing: the root lock doesn't hold b
cd packages/b && socket-patch scan --mode hosted --json --yes   # (patch API mock serving left-pad@1.3.0)
# -> status error, redirect_workspace_lockfile_elsewhere, exit 1
socket-patch scan --mode vendored --json --yes
# -> failed vendor_lockfile_missing, exit 1

Delete bunfig.toml (so Bun writes a text bun.lock with configVersion) and both runs pin packages/b/bun.lock, exit 0.

Expected vs actual

CLI_CONTRACT (redirect_workspace_lockfile_elsewhere) says ! patterns are read "the way the root's lock's package manager reads them". Bun 1.3+ applies !, and the member is installed from its own lock (verified: bun install --frozen-lockfile in packages/b installs from packages/b/bun.lockb). Expected: the member's own lock is pinned, as in 5.0.0. Actual: a refusal that names a lock Bun never reads for this member. The same contract line equates "bun.lockb alone" with older Bun, and that premise is false for Bun 1.2+ projects that use saveTextLockfile = false.

Matrix (scan run in the excluded member packages/b)

Bun root lock hosted vendored
1.4.3 bun.lockb fail (refused) fail (vendor_lockfile_missing)
1.3.9 bun.lockb fail fail
1.4.3 text bun.lock pass (pins member lock) pass
1.3.9 text bun.lock pass pass
1.2.23 bun.lockb / text n/a: Bun < 1.3 ignores !, so b really is a member (correct) n/a

Only Linux was tested. macOS and Windows are untested, but the dialect choice doesn't depend on the OS.

First bad

v5.0.0 release binary: hosted pins packages/b/bun.lockb, and a fresh frozen install gives patched bytes. v5.0.1 release binary: refused. The change comes from #1480 (1b5d8d7, "Fix workspace member globs for yarn 1 and npm (#1097)").

Suspect code

crates/socket-patch-core/src/hosted/governing_root.rs:576 (BUN_LOCKB => WorkspacesDialect::BUN_PRE_1_3). A possible fix: read the cNfGvRsN trailer tag from bun.lockb and treat it the way configVersion is treated in bun.lock.

Activity

  1. mikolalysenko commented on Oct 13, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged as priority:p1 (Bun, a 5.0.1 regression). Not the same root cause as #1490: here lock_dialect in hosted/governing_root.rs reads every lone bun.lockb as pre-1.3 Bun, while #1490 is the glob grammar in utils/workspace_globs.rs. Left eligible for a separate fix.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 13, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: lock_dialect reads every lone bun.lockb as pre-1.3 Bun). Branch: agent/fix-bun-lockb-dialect. Claim-ID: 2026-10-13T18:21:06Z-b6cde9


    Generated by Claude Code

  3. mikolalysenko commented on Oct 13, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1496


    Generated by Claude Code

  4. mikolalysenko commented on Oct 14, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Bun bug-hunt re-check on bb73e2f (Linux): #1496 fixes this for locks written by Bun 1.3.9 and 1.4.3, bun.lockb and text. From the !-excluded member, hosted pins its own lock and vendored vendors it, and a fresh frozen clone is patched. A 1.2.23 bun.lockb upgraded by a 1.4.3 bun install gains cNfGvRsN and passes too.

    What's left is filed as #1521: Bun 1.3.0 and 1.3.1 honour ! but write neither configVersion nor cNfGvRsN, so their locks (text and lockb) are still refused.


    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