pnpm 11 workspace: collector drops dependencies due to cross-project deduped `pnpm list` output (Cannot find module 'mime-types' / 'mime-db')

Author: nogodCreated Jul 8, 2026Updated Sep 7, 2026
LabelsStale

Summary

In a pnpm 11 workspace, the pnpm node-module collector silently drops packages from app.asar, and the packaged app crashes on launch with Cannot find module 'mime-types'. The cause is pnpm 11's pnpm list --json output, which dedupes entries across workspace projects: for a given name@version, only ONE occurrence anywhere in the workspace carries the fully-expanded subtree; every other occurrence is a stub with dedupedDependenciesCount > 0 and no children.

Two distinct collector behaviors interact badly with this format:

  1. Released 26.15.3 / 26.15.6: the collectedDeps visited-set (introduced in #9732, same family as #9903) prunes by name@version. If the first visited occurrence of an intermediate package (e.g. @trpc/server) is one whose own children are deduped stubs, the later occurrence that carries the real expanded subtree is skipped entirely — everything below it is never collected. In our tree this drops 7 packages: mime-types, mime-db, get-intrinsic, es-object-atoms, call-bind-apply-helpers, dunder-proto, get-proto. They are logged under "unresolved duplicate dependency references" / "cannot find path for dependency" and omitted from the asar.

  2. Current master (7a0abca): much better — 6 of the 7 come back — but it still drops mime-db, the runtime dependency of mime-types. The app's own mime-types entry is a deduped stub, so in extractProductionDependencyGraph the on-disk package.json is used for the all check, but the children iterated are still the stub's (empty) tree.dependencies — so mime-db is never linked into the production graph. It also isn't reachable any other way: mime-db sits as a top-level entry of the app project in the pnpm list output (pnpm 11 hoisted-dedupe placement), where the graph extraction filters it out because it isn't a declared dependency of the app's package.json. Packaged app then fails with Cannot find module 'mime-db' (required from mime-types/index.js).

Note: adding the missing package as a direct dependency of the app does not help — our mime-types was a direct dependency (pinned 2.1.35) and was still dropped, because its entry in the app's tree is a deduped stub either way.

Environment

  • electron-builder / app-builder-lib: 26.15.3 and 26.15.6 (also reproduced against master @ 7a0abca via the collector's getNodeModules() directly)
  • pnpm: 11.9.0 (worked fine before upgrading from pnpm 10.34.4 — same repo, same electron-builder)
  • pnpm workspace monorepo (Next.js apps + packages + one Electron app project), default virtual-store layout (no node-linker=hoisted)
  • Affects both macOS and Windows packaging: verified on both platforms (macOS 15 universal dmg/zip, and Windows NSIS) — the packaged artifacts are missing the identical set of modules.

Shape of the pnpm 11 list output that triggers it

The Electron app (electron/) depends on axiosform-data[email protected], and directly on [email protected]. Another workspace project (apps/web) also reaches [email protected] via @trpc/server > express > accepts. pnpm 11 emits the full expansion only once, in the web project's tree:

web > @trpc/client > @trpc/server > express > accepts > [email protected]   ← only full entry (has children)
web > api > @trpc/server                                                    ← visited FIRST; its express child is a deduped stub
app > [email protected]   { dedupedDependenciesCount: 1 }        ← stub, no children
app > [email protected]                                             ← full, but top-level of the project ⇒ filtered (not a declared dep)

On 26.15.x, @trpc/[email protected] gets marked visited at web > api > @trpc/server (children deduped), so web > @trpc/client > @trpc/server — the copy that actually contains express > accepts > mime-types — is skipped by the visited-set, and [email protected] ends up with no full entry collected at all.

Results (running getNodeModules({packageName}) against our workspace)

collector result
26.15.3 / 26.15.6 release ❌ drops mime-types, mime-db, get-intrinsic, es-object-atoms, call-bind-apply-helpers, dunder-proto, get-protoCannot find module 'mime-types' on launch
master @ 7a0abca ❌ still drops mime-dbCannot find module 'mime-db' on launch
26.15.3 + patch below ✅ all packages collected, no "unresolved duplicate" warnings

Workaround we're shipping (26.15.3, via pnpm patch)

In pnpmNodeModulesCollector.js collectDepsRecursively, re-walk children of already-visited nodes instead of pruning the whole subtree (the visited-set still prevents re-registering/re-locating; since each JSON node is structurally visited at most once, this stays O(total nodes)):

javascript
if (this.collectedDeps.has(id)) {
    // pnpm v11 dedupes the list output: the fully-expanded subtree for a given
    // name@version can appear at a later occurrence than the first one visited.
    // Re-walk this occurrence's children (without re-registering the package)
    // so those subtrees are not lost.
    if (value.dependencies || value.optionalDependencies) {
        await this.collectDepsRecursively(value);
    }
    return;
}

For master, the remaining gap is different: when a deduped stub's dependencies are discovered from the on-disk package.json (the all map), the children that get linked into the production graph should come from that map too (or from the deduped target's real entry), not from the stub's empty tree.dependencies.

Suggested regression test

A workspace fixture with two projects where (a) project B contains the only full expansion of a shared name@version behind an intermediate package whose first-visited occurrence has deduped children, and (b) the app's own direct dep is a deduped stub whose transitive dep (mime-typesmime-db) only exists elsewhere as a project-top-level dedupe placement. Must be generated with pnpm ≥ 11 (pnpm list --prod --json --depth Infinity output differs fundamentally from pnpm 10).

Happy to test a fix branch against our workspace and report back.

Source: electron-userland/electron-builder