Repository navigation
Bun 1.3.0 and 1.3.1 locks have no configVersion, so a "!"-excluded member is still refused in hosted and vendored mode after the #1492 fix (regression since #1480) #1521
Description
[agent] Found by the scheduled Bun bug-hunt routine (ledger #306).
Summary
#1480 (#1097) decides whether a Bun workspace root honours ! patterns from the root lock. A bun.lock with a top-level configVersion, or a bun.lockb with the cNfGvRsN trailer (added by #1496 for #1492), counts as Bun 1.3+. Anything else counts as pre-1.3, which ignores !. But Bun 1.3.0 and 1.3.1 already honour !, and neither writes configVersion or the cNfGvRsN trailer. Bun 1.3.2 is the first release that writes them. So the #1492 symptom is still there for every lock written by 1.3.0 or 1.3.1, text and binary alike:
- A hosted
scanfrom the excluded member (which has its own lock) fails withredirect_workspace_lockfile_elsewhere. The message is false: it says Bun "installs it from /bun.lock and ignores its own ./bun.lock", but the root lock doesn't list the member at all. - A vendored
scanfrom the member fails withvendor_lockfile_missing. - The suggested root run exits 0 with
successand skips the package asredirect_unconfirmed. Nothing gets pinned anywhere.
These locks stay untagged on newer Bun as long as CI only runs bun install --frozen-lockfile. A plain bun install on 1.3.2+ adds "configVersion": 0 and works around it.
Impact
On Bun 1.3.0/1.3.1, and on any project whose lock was last written by those versions, hosted and vendored modes can't patch a project excluded with !, from either directory. 5.0.0 and the commit before #1480 patch it fine.
Repro (Linux, socket-patch bb73e2f, Bun 1.3.0)
mkdir -p w/packages/a w/packages/excluded && cd w && git init -q
echo 'node_modules/' > .gitignore
echo '{"name":"root","private":true,"workspaces":["packages/*","!packages/excluded"],"dependencies":{"minimist":"1.2.5"}}' > package.json
echo '{"name":"a","version":"1.0.0","dependencies":{"minimist":"1.2.5"}}' > packages/a/package.json
echo '{"name":"excluded","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > packages/excluded/package.json
bun install && (cd packages/excluded && bun install) # Bun 1.3.0
grep -c '"packages/excluded"' bun.lock # 0: Bun excluded it
grep -c configVersion bun.lock # 0: untagged
git add -A && git commit -qm init
cd packages/excluded && socket-patch scan --mode hosted --json
# status "error", error.code redirect_workspace_lockfile_elsewhere, exit 1
socket-patch scan --mode vendored --json # partialFailure, vendor_lockfile_missing, exit 1
cd ../.. && socket-patch scan --mode hosted --json # success, left-pad skipped redirect_unconfirmed, exit 0For the bun.lockb variant, add bunfig.toml with [install] saveTextLockfile = false at the root and in the member. Patch data came from a local mock of the patch API (--api-url <mock> --org o --api-token fake, with SOCKET_PATCH_SERVER_URL set to the mock). The oracle was a fresh clone plus a cold-cache bun install --frozen-lockfile in packages/excluded, then checking node_modules/left-pad/index.js for the patch marker.
Expected vs actual
CLI_CONTRACT.md (redirect_workspace_lockfile_elsewhere) says the refusal applies only when "the nearest ancestor package.json whose workspaces … matches the directory", and that "a ! pattern excludes what earlier patterns matched for … Bun 1.3+". The member isn't in Bun 1.3.0's workspace, so it's a standalone project, and its own lock should be pinned, as it is on 1.3.2+. Actual: the member is refused, and the root run reports success without pinning anything.
Matrix (Linux, 2 runs each)
| Bun writing the locks | lock | member hosted | member vendored | root hosted | frozen clone of member |
|---|---|---|---|---|---|
| 1.2.23 | text / lockb | n/a (Bun ignores !; the member really is a member) |
n/a | — | — |
| 1.3.0 | text / lockb | refused | refused | success, nothing pinned | unpatched |
| 1.3.1 | text / lockb | refused | refused | success, nothing pinned | unpatched |
| 1.3.2 / 1.3.3 | text / lockb | (writes configVersion / cNfGvRsN) |
|||
| 1.3.9 / 1.4.3 | text / lockb | pinned | vendored | — | patched |
1.2.23 lockb upgraded by a 1.4.3 bun install |
lockb | pinned | — | — | patched |
macOS and Windows are untested; the logic doesn't depend on the OS.
First bad commit
1b5d8d7~1, the commit before Fix workspace member globs for yarn 1 and npm (#1097) #1480: 1.3.0 and 1.3.1 text and lockb are pinned, and the frozen clone is patched.1b5d8d7(Fix workspace member globs for yarn 1 and npm (#1097) #1480) throughbb73e2f, including release 5.0.1 and the Fix Bun 1.3+ bun.lockb read as pre-1.3 (#1492) #1496 fix: refused.
Suspect code
crates/socket-patch-core/src/hosted/governing_root.rs:555-590 (lock_dialect). The doc comment says "a lock without it, as Bun 1.3.0 and earlier write, reads as older Bun", but 1.3.0 and 1.3.1 honour !. One possible signal for an untagged lock: whether the root lock's workspaces map (or the lockb workspace list) actually contains the candidate directory. Bun ≤ 1.2 always lists a matched member there, and 1.3.0/1.3.1 don't list an excluded one.
Activity
bun: run 41 ledger (#1521), npm handover
mikolalysenko commentedon Oct 14, 2026
[agent] Triage:
priority:p1(Bun, npm family). This is not a duplicate. #1492 / #1496 taughtlock_dialectto read thecNfGvRsNtrailer in abun.lockb, but neither covers locks that Bun 1.3.0 / 1.3.1 write with no tag at all. No open PR covers it.Confirmed in code on main
313d532:crates/socket-patch-core/src/hosted/governing_root.rslock_dialectreturnsBUN_PRE_1_3for anybun.lockwithout a top-levelconfigVersionand any untaggedbun.lockb.package_json_workspace_rootthen counts the!-excluded member as governed by the root lock. Unclustered: the cause is local to the Bun dialect probe.Generated by Claude Code
mikolalysenko commentedon Oct 14, 2026
[agent] Claiming this issue (shared root cause: untagged Bun 1.3.0/1.3.1 locks read as the pre-1.3 workspaces dialect). Branch: agent/fix-bun-untagged-lock-dialect. Claim-ID: 2026-10-14T06:21:27Z-5cc33c
Generated by Claude Code
mikolalysenko commentedon Oct 14, 2026
[agent] Draft PR: #1523
Generated by Claude Code
Honour Bun 1.3.0/1.3.1 `!` workspace exclusions
Pin the #1521 test to each lock's workspace list
Read the Bun version once per untagged lock
mikolalysenko commentedon Oct 14, 2026
[agent] Verified fixed by #1523 on main
a3f56f7with real Bun on Linux. The setup is a!packages/b-excluded member that has its own lock, scanned from the member:--frozen-lockfilein a fresh clonebun.lockbbun.lockbNo refusal fired (
redirect_workspace_lockfile_elsewhere/vendor_lockfile_missing), and the root lock was left untouched.Generated by Claude Code