Skip to content

bun prune/bun dedupe: false "bun.lock does not match package.json" when a workspace package is both a dependency and a peer dependency #40366

Closed

Description

@jakeboone02

What version of Bun is running?

1.4.0+34cbb9a40

What platform is your computer?

Confirmed on two platforms:

  • Linux 6.18.33.2-microsoft-standard-WSL2 x86_64 x86_64
  • Darwin 25.6.0 arm64 arm

What steps can reproduce the bug?

bun prune and bun dedupe both reject a lockfile that bun install considers current, whenever a workspace package lists another workspace package in both a normal dependency field and peerDependencies.

Three files:

// package.json
{ "name": "root", "private": true, "workspaces": ["a", "b"] }
// a/package.json
{ "name": "wa", "version": "1.0.0" }
// b/package.json
{
  "name": "wb",
  "version": "1.0.0",
  "devDependencies": { "wa": "workspace:*" },
  "peerDependencies": { "wa": "workspace:*" },
}
bun install
bun prune
bun dedupe

What is the expected behavior?

bun prune and bun dedupe should agree with bun install that the lockfile is up to date, and proceed.

What do you see instead?

bun install writes the lockfile and reports no changes:

Saved lockfile
Checked 4 installs across 3 packages (no changes) [4.00ms]

Immediately afterwards, both new 1.4 commands claim the lockfile is stale:

$ bun prune
bun prune v1.4.0 (34cbb9a40)

error: bun.lock does not match package.json, nothing to prune
note: run 'bun install' first
exit=1

$ bun dedupe
bun dedupe v1.4.0 (34cbb9a40)

error: bun.lock does not match package.json, nothing to dedupe
note: run 'bun install' first
exit=1

Following the note (bun install) does not help — the state is already converged. bun install --frozen-lockfile exits 0 on the same tree, so install and prune/dedupe disagree about whether the lockfile matches:

$ bun install --frozen-lockfile
Checked 4 installs across 3 packages (no changes) [6.00ms]
exit=0

The generated lockfile records the dependency once in each field, which looks correct:

"b": {
  "name": "wb",
  "version": "1.0.0",
  "devDependencies": { "wa": "workspace:*" },
  "peerDependencies": { "wa": "workspace:*" },
},

Additional information

Isolated by bisection. The trigger is specifically a workspace-resolved dependency that is also declared as a peer:

b's declaration of the dependency bun prune
peerDependencies only (workspace pkg) OK
dependencies + peerDependencies (workspace pkg, exact version 1.0.0) fails
devDependencies + peerDependencies (workspace pkg, exact version 1.0.0) fails
devDependencies + peerDependencies (workspace pkg, workspace:*) fails
devDependencies + peerDependencies (registry pkg, is-odd@3.0.1) OK

So it is not peer deps in general and not workspace deps in general — only their combination. Both the workspace:* protocol and a plain exact version that happens to resolve to a workspace trigger it. My guess is that the manifest-vs-lockfile comparison in prune/dedupe counts the dependency twice on the manifest side while the lockfile deduplicates it into one resolution, so the counts never line up.

This is worth flagging because the pattern is the normal way to publish a plugin/adapter package: the adapter peer-depends on the core package for consumers, and dev-depends on the same workspace copy to build and test against. I hit it in a monorepo where every UI-integration package does exactly this, which makes bun prune and bun dedupe unusable there. Other lockfile consumers in the same tree (bun install, bun install --frozen-lockfile, bun outdated, bun pm ls) are all fine.

Also confirmed against a lockfile deleted and regenerated from scratch, so it is not a stale-lockfile artifact — the freshly written bun.lock is rejected by the command that would have to read it next.

(This is not a duplicate of 38912 — that one is bun prune declining to remove a leftover isolated store after a linker switch (error: node_modules was installed with the isolated linker). This is a different check with a different message, and reproduces on a fresh hoisted-only install with no linker change.)

Activity

  1. robobun commented on Aug 24, 2026

    @robobun
    Collaborator

    Reproduced: with a workspace package listed in both a dependency group and peerDependencies, bun prune and bun dedupe reject the lockfile bun install just wrote. The bun.lock loader skips resolving the duplicate peer entry to the workspace path, so the diff against package.json never matches. The open PR #38869 fixes this exact loader bug (it surfaced there as bun install re-resolving a workspace on every run). A regression test for the prune/dedupe symptom is on the branch farm/a9e82d7a/prune-dedupe-workspace-peer-dup.

  2. added a commit that references this issue on Sep 4, 2026
    f942cb0
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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions