Repository navigation
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
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 hostedrun inpackages/brefuses withredirect_workspace_lockfile_elsewhere. Its message is wrong: "Bun installs it from /bun.lockb and ignores its own ./bun.lockb". Exit 1.scan --mode vendoredrun inpackages/bfailsvendor_lockfile_missingwith 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 withredirect_unconfirmed. A vendored run fails withvendor_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 1Delete 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
mikolalysenko commentedon Oct 13, 2026
[agent] Triaged as priority:p1 (Bun, a 5.0.1 regression). Not the same root cause as #1490: here
lock_dialectinhosted/governing_root.rsreads every lonebun.lockbas pre-1.3 Bun, while #1490 is the glob grammar inutils/workspace_globs.rs. Left eligible for a separate fix.Generated by Claude Code
mikolalysenko commentedon Oct 13, 2026
[agent] Claiming this issue (shared root cause:
lock_dialectreads every lonebun.lockbas pre-1.3 Bun). Branch: agent/fix-bun-lockb-dialect. Claim-ID: 2026-10-13T18:21:06Z-b6cde9Generated by Claude Code
mikolalysenko commentedon Oct 13, 2026
[agent] Draft PR: #1496
Generated by Claude Code
Read Bun's config-version tag from bun.lockb
Honour "!" workspaces under a Bun 1.3+ bun.lockb
mikolalysenko commentedon Oct 14, 2026
[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.lockband text. From the!-excluded member, hosted pins its own lock and vendored vendors it, and a fresh frozen clone is patched. A 1.2.23bun.lockbupgraded by a 1.4.3bun installgainscNfGvRsNand passes too.What's left is filed as #1521: Bun 1.3.0 and 1.3.1 honour
!but write neitherconfigVersionnorcNfGvRsN, so their locks (text and lockb) are still refused.Generated by Claude Code