Skip to content

Vendored bun.lockb workspaces write the member-relative tarball mirrors with no .gitignore check, so a *.tgz rule drops them from the commit and fresh frozen installs fail (Bun 1.1.39–1.2.23) or hang (1.3.9) #1116

Description

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

Summary

On a bun.lockb workspace, scan --mode vendored commits the patched tarball twice: once at the canonical root artifact .socket/vendor/npm/<uuid>/<name>-<ver>.tgz, and once as a member-relative mirror at packages/<member>/.socket/vendor/npm/<uuid>/<name>-<ver>.tgz (bun_binary.rs:141-144: "Bun 0.5.9–1.3 resolves workspace local tarballs relative to the declaring member").

The #831 fix (#837) protects only the root artifact. It probes git check-ignore on the root uuid dir and writes a <uuid>/.gitignore containing !* there. The member mirrors get neither treatment. So with GitHub's stock Node .gitignore rule *.tgz (or a member-local .gitignore with *.tgz or .socket/), the scan exits 0 with status: success and no warning, but git ignores the mirror (!! packages/a/.socket/) and it never gets committed.

Impact

In every fresh checkout of the committed state, when the member resolves its own copy of the patched package (for example, the root depends on left-pad@1.2.0 and member a on left-pad@1.3.0):

  • Bun 1.1.39 / 1.1.45 / 1.2.23: bun install --frozen-lockfile fails with error: ENOENT extracting tarball from left-pad.
  • Bun 1.3.9: bun install --frozen-lockfile hangs. It was still running after 20 minutes, and it was killed by a 40 s timeout on the re-run.
  • Bun 1.4.2: the install succeeds, because 1.4 resolves root-relative. But in the clone, vendor --check exits 1 (vendor_check_failed, Corrupt { reason: "vendor_workspace_artifact_missing" }) and vex refuses the patch (vendor_workspace_artifact_missing, exit 1). So the CI gate stays red on every clone. repair rebuilds the mirror, but the mirror is still ignored, so it can never be committed without git add -f.

This affects the same population as #831: the *.tgz rule ships in GitHub's Node.gitignore.

Repro (Linux)

I used a local mock of the patch proxy (SOCKET_PROXY_URL / SOCKET_PATCH_SERVER_URL) that serves left-pad@1.3.0 with /* SOCKET-PATCHED-A */ prepended to index.js.

mkdir -p ws/packages/a && cd ws && git init -q
echo '{"name":"root","private":true,"workspaces":["packages/*"],"dependencies":{"left-pad":"1.2.0"}}' > package.json
echo '{"name":"a","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > packages/a/package.json
printf '[install]\nsaveTextLockfile = false\n' > bunfig.toml    # bun.lockb (the default on 1.1.x)
printf 'node_modules\n*.tgz\n' > .gitignore
bun install && git add -A && git commit -qm init
socket-patch scan --mode vendored --json     # exit 0, status success, no ignore warning
git status --short --ignored                 # M bun.lockb / ?? .socket/ / !! packages/a/.socket/
git add -A && git commit -qm vendored        # the mirror packages/a/.socket/... is not committed
git clone -q . ../fresh && cd ../fresh
bun install --frozen-lockfile                # 1.1.39-1.2.23: ENOENT extracting tarball; 1.3.9: hangs
socket-patch vendor --check                  # (1.4.2) exit 1, vendor_workspace_artifact_missing

Control: the same steps without the *.tgz line commit packages/a/.socket/vendor/npm/<uuid>/left-pad-1.3.0.tgz, and every version's fresh frozen install gets the patched bytes.

Expected vs actual

  • Expected: the same treatment Fix vendored npm-family tarballs dropped by .gitignore (#831) #837 gives the root artifact (CLI_CONTRACT.md vendor_artifact_gitignored). Where a nested rule can override the ignore (*.tgz), each mirror's <uuid>/ dir should get its own !* .gitignore. Where it can't (for example, a member-local .socket/ rule), the scan should refuse before any write. vendor is documented as producing a committable .socket/vendor/.
  • Actual: exit 0 with no warning. The mirror is git-ignored, and fresh checkouts fail, hang, or fail vendor --check.

Matrix

Linux, main 829d0af. Nested shape: root left-pad@1.2.0, member left-pad@1.3.0, root .gitignore *.tgz. Each cell shows the scan exit / the fresh clone's bun install --frozen-lockfile.

Bun *.tgz ignored Control (no ignore)
1.1.39 0 / ENOENT, exit 1 0 / patched
1.1.45 0 / ENOENT, exit 1 0 / patched
1.2.23 0 / ENOENT, exit 1 (×2) 0 / patched
1.3.9 0 / hangs (×2) 0 / patched
1.4.2 0 / patched, but vendor --check and vex fail in the clone 0 / patched, --check ok

Other ignore shapes on 1.4.2, with a deduped member and a plain workspace:

  • Member-local packages/a/.gitignore with *.tgz or with .socket/: same result. The scan succeeds, the mirror is ignored, and vendor --check fails in the clone.
  • Root vendor/: correctly refused (the root probe catches it).
  • In the deduped shape (root and member on the same version), Bun 1.1.39–1.4.2 install fine without the mirror. Only vendor --check / vex break there.
  • Text bun.lock workspaces write no mirror, so they're unaffected.

I haven't run macOS or Windows (git's ignore semantics are the same there). This isn't a regression in the usual sense: release 4.0.0 refused vendored bun.lockb entirely, and the mirrors predate the #837 fix, which simply didn't cover them.

Suspect code


Backlog review — 2026-10-08

Priority: P1 → P2. Ignored mirror tarballs break fresh installs for older binary-lock Bun workspace layouts. Keep the gitignore guard; visible install failure and narrow layout.

Activity

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