Skip to content

Yarn2 analyzer: "Could not unambiguously resolve locator" on valid multi-version / compat-patch lockfiles #12008

Description

@gormed

Describe the bug

ORT's Yarn 2+ analyzer treats multiple co-installed versions of the same package as a fatal resolution error (IllegalStateException: Could not unambiguously resolve locator …), even when the project is in a valid state according to Yarn Berry.

This is especially problematic for packages that Yarn automatically rewrites to builtin compat patches (e.g. resolve via @yarnpkg/plugin-compat). A single logical dependency can legitimately exist as both:

  • resolve@npm:<version>
  • resolve@patch:resolve@npm:<version>#optional!builtin<compat/resolve>

... and different parts of the tree may still use different semver majors (e.g. resolve@1.x and resolve@2.0.0-next.x). That is normal Yarn Berry behavior and should not abort analysis.

ORT appears to look up an exact locator (often derived from a lockfile descriptor or patch URL) and fails if that exact identity is not the only installed variant, instead of accepting the installed set Yarn actually linked.

To Reproduce

Steps to reproduce the behavior:

  1. Use a Yarn 4 (Berry) project with nodeLinker: node-modules and a dependency that pulls in resolve (e.g. via rechoirknex, or eslint with resolve@^2.0.0-next).
  2. Ensure the lockfile contains both plain npm and compat-patched resolve locators, and optionally multiple majors, e.g.:
    • resolve@npm:^1.20.01.22.12
    • resolve@npm:^2.0.0-next.52.0.0-next.7
    • resolve@patch:resolve@npm%3A^1.20.0#optional!builtin<compat/resolve>…::version=1.22.12&hash=…
    • resolve@patch:resolve@npm%3A^2.0.0-next.5#optional!builtin<compat/resolve>…::version=2.0.0-next.7&hash=…
  3. Run ORT analyzer on the project (ort analyze … with Yarn2 package manager enabled).
  4. See error similar to:
Yarn 2+ failed to resolve dependencies for path '<package>/package.json': IllegalStateException: Could not unambiguously resolve locator 'resolve@patch:resolve@npm%3A2.0.0-next.5#optional!builtin<compat/resolve>::version=2.0.0-next.5&hash=c3c19d'. Found 4 installed versions of module 'resolve': [resolve@npm:1.22.12, resolve@npm:2.0.0-next.7, resolve@patch:resolve@npm%3A1.22.12#optional!builtin<compat/resolve>::version=1.22.12&hash=c3c19d, resolve@patch:resolve@npm%3A2.0.0-next.7#optional!builtin<compat/resolve>::version=2.0.0-next.7&hash=c3c19d]

Expected behavior

  • ORT should not fail analysis when Yarn Berry has successfully installed a dependency tree with multiple versions or multiple locators for the same package name.
  • The analyzer should map lockfile descriptors / patch locators to the actually installed packages (as yarn why / the lockfile resolution: lines show), similar to how npm/yarn tolerate multiple versions in large monorepos.
  • Builtin compat patches (optional!builtin<compat/resolve>) should be first-class citizens in locator resolution, not treated as "missing" versions.

Console / log output

We have several of those:

Yarn 2+ failed to resolve dependencies for path '<package>/package.json': IllegalStateException: Could not unambiguously resolve locator 'resolve@patch:resolve@npm%3A2.0.0-next.5#optional!builtin<compat/resolve>::version=2.0.0-next.5&hash=c3c19d'. Found 4 installed versions of module 'resolve': [resolve@npm:1.22.12, resolve@npm:2.0.0-next.7, resolve@patch:resolve@npm%3A1.22.12#optional!builtin<compat/resolve>::version=1.22.12&hash=c3c19d, resolve@patch:resolve@npm%3A2.0.0-next.7#optional!builtin<compat/resolve>::version=2.0.0-next.7&hash=c3c19d]

Corresponding <package>/yarn.lock entries:

"resolve@npm:^1.20.0":
  resolution: "resolve@npm:1.22.12"

"resolve@npm:^2.0.0-next.5":
  resolution: "resolve@npm:2.0.0-next.7"

"resolve@patch:resolve@npm%3A^1.20.0#optional!builtin<compat/resolve>":
  resolution: "resolve@patch:resolve@npm%3A1.22.12#optional!builtin<compat/resolve>::version=1.22.12&hash=c3c19d"

"resolve@patch:resolve@npm%3A^2.0.0-next.5#optional!builtin<compat/resolve>":
  resolution: "resolve@patch:resolve@npm%3A2.0.0-next.7#optional!builtin<compat/resolve>::version=2.0.0-next.7&hash=c3c19d"

yarn install succeeds; yarn why resolve shows expected patched/unpatched usage.

Environment

Based on a run from @oheger-bosch - i hope he has the details.

Additional context

  • Yarn Berry's @yarnpkg/plugin-compat automatically applies optional!builtin<compat/resolve> to resolve dependencies (plugin-compat README). This is unrelated to user-defined resolutions and cannot be removed without disabling compat.
  • Even with resolutions pinning resolve to a single version, both resolve@npm:<ver> and resolve@patch:…::<ver> can remain in the lockfile — ORT still errors.
  • Related upstream work: ort#8058, ort#11208.
  • Workaround today: per-package resolutions to collapse semver descriptor buckets (e.g. "debug": "^4.4.3", "@babel/parser": "^7.29.3"). This is fragile, does not scale across large monorepos, and does not fix the npm + compat-patch dual-locator case for resolve.
  • We hit this across 50+ package.json workspaces in a Yarn 4 monorepo during OSS compliance (OCaaS) scanning.

Metadata

Metadata

Assignees

No one assigned

    Labels

    analyzerAbout the analyzer toolto triageIssues that need triaging

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions