Resolver
Understand version selection, peer dependencies, overrides, and resolution failures.
The resolver selects exact package versions and connects each dependency to the package instance that satisfies it. Use this page to understand unexpected versions, peer conflicts, and failed installs.
LPM CLI resolves packages from npm, LPM.dev Registry, and private registries.
Registry configuration controls routing and authentication. Only @lpm.dev/* packages always use LPM.dev Registry.
Quickstart
# Resolve and install the project's declared dependencies
lpm install
# Inspect candidate packages without changing the project
lpm resolve 'react@^19' 'react-dom@^19' --json
# Explain a package in the existing project graph
lpm graph --why react
# Require the committed graph in CI
lpm cilpm resolve uses the supplied package specs and default resolution policies. It does not apply the project's dependency declarations, catalogs, overrides, or peer rules. lpm install applies those project settings, so its result can differ.
Configuration
lpm.json has no dedicated resolver settings.
Project resolution settings belong in package.json > lpm:
{
"lpm": {
"autoInstallPeers": true,
"strictPeerDependencies": true,
"overrides": {
"example-parent>example-child": "^2.0.0"
}
}
}For a repeatable candidate check, use an uncached task:
{
"tasks": {
"check-candidates": {
"command": "lpm resolve \"react@^19\" \"react-dom@^19\"",
"cache": false
}
}
}lpm run check-candidates -- --jsonTask caching stays off, but registry metadata caches still apply. See Task runner for task configuration.
Locked installs and new selections
An eligible lockfile replays exact package instances instead of selecting new versions. A newer registry release alone does not replace a locked version. Use lpm upgrade to update selected dependencies.
For a new resolution, the default resolver prefers a compatible latest version or the requested dist-tag.
It then considers other matching versions in descending version order, subject to install policy.
The numerically highest release is not always the selected release.
For ordinary transitive dependencies, a compatible version already selected by a manifest dependency can take precedence. Otherwise, reuse requires the selected version to match. Overrides can require a separate selection.
Several versions of the same package can coexist. For example, dependencies that require helper@^1 and helper@^2 need separate versions.
Different peer contexts can also require separate instances of the same name and version.
See Dependency graph for the distinction between a package version and an installed instance.
Resolver modes
| Mode | Selection |
|---|---|
| Greedy-fusion | Default. Selects versions as metadata becomes available and supports multiple versions and automatic peer installation. |
| PubGrub with split retries | Optional alternative. Retries conflicts with separate package selections, but does not automatically install missing peers. |
LPM_RESOLVER=pubgrub lpm installThe environment variable applies to this command. Remove it to restore the default. A locked install can reuse its graph without running either resolver.
PubGrub stops when a conflict report provides no new package to split. It does not guarantee a solution for every satisfiable graph. Its wildcard requests can select prereleases. The default resolver excludes prereleases from bare wildcard requests. If you need a prerelease, use an explicit prerelease range or tag.
Peer and optional dependencies
The default resolver automatically installs missing required peers when autoInstallPeers is enabled.
These packages can appear at the project root without an entry in package.json.
Optional peers are not automatically installed. They can bind to a compatible version already present in the graph.
| Setting | Precedence and default |
|---|---|
| Automatic peers | lpm.autoInstallPeers, then user auto-install-peers, then true. |
| Strict peers | CLI flag, then lpm.strictPeerDependencies, then user strict-peer-dependencies, then false. |
# Fail on missing required peers, mismatches, and conflicting requirements
lpm install --strict-peer-dependencies
# Use warnings for this invocation
lpm install --no-strict-peer-dependenciesPubGrub does not add missing peers, even when automatic peers are enabled.
Strict peer checks still apply to the resulting graph.
A required peer with no available candidate can fail resolution before these checks.
peerDependencyRules changes post-resolution warnings. It does not change automatic peer selection or conflict detection.
With the default linker selection, peer conflicts can switch an install to the isolated layout.
An explicit --linker flag or persistent linker setting prevents this automatic switch.
Isolation does not make incompatible peer ranges compatible.
Optional dependencies can be skipped when resolution, download, platform, or engine checks reject them. If a required dependency also reaches the package, required-dependency rules apply. See lpm install for peer configuration and diagnostics.
Overrides and aliases
Override sources have this precedence:
| Source | Priority |
|---|---|
package.json > lpm > overrides | Highest |
package.json > overrides | Middle |
package.json > resolutions | Lowest |
{
"lpm": {
"overrides": {
"example-child": "^2.0.0",
"example-parent>other-child": "3.1.0"
}
}
}A plain package selector applies throughout the graph.
Plain selectors and unqualified parent selectors can replace a range that has no available version.
parent>child targets a direct parent-child edge. A version selector, such as child@<2, matches the naturally selected version.
A range-qualified selector cannot match without a natural candidate.
Multi-level paths such as a>b>c are rejected.
Override targets must be strings. Nested npm override objects produce a warning and are not applied.
Release-age, trust, engine, and platform checks still apply to overridden versions. If an override target is unavailable or rejected during selection, LPM CLI warns and keeps the natural selection. If neither selection is available, required dependencies fail and optional dependencies can be skipped.
Override summaries omit unchanged selections. A rescued selection shows unresolved as its original version.
JSON output and saved override records use from_version: null for this case.
See Override selectors for the supported forms.
An npm alias gives a dependency a local name:
{
"dependencies": {
"react-previous": "npm:react@^18"
}
}The alias follows the target package's registry route. The lockfile preserves the alias and its exact target for later installs.
Policies that affect the result
| Policy | Effect |
|---|---|
| Minimum release age | Off by default. When enabled, checks direct dependencies; strict policy also checks transitive dependencies and lockfile replay. |
| Trust policy | Can reject candidates that reduce trusted-publisher evidence. Fetch-time checks can also require previously verified provenance. |
| Engines | Required incompatible packages fail by default. Optional-only incompatible packages can be skipped. |
| Platform | Preserves os, cpu, and libc metadata in the lockfile, then checks compatibility for the current host. |
During resolution, ranges can select an older version that passes release-age or publisher-trust checks. Fetch-time provenance failures do not trigger version reselection. A blocked exact pin fails instead of silently changing its version. See Install policies, Trust, and Security and audit.
Diagnose a failed or unexpected selection
lpm graph --why example-child
lpm install --strict-peer-dependencies --json
lpm resolve 'example-child@^2' --jsonResolution errors identify the requested package, range, and parent when that information is available.
Structured resolution failures use error_code: "resolution_failed". The nested error.kind describes the cause.
Strict peer-check failures use error_code: "peer_dependency".
| Cause | Recovery |
|---|---|
| No matching version or no solution | Check the range, tag, registry route, and override selectors. |
| Policy rejection | Read the named policy and choose an allowed version. |
| Registry request failure | Check registry availability and credentials before retrying. |
| Peer conflict | Align the consumer ranges or explicitly declare a compatible peer. |
| Platform or engine mismatch | Use a supported environment or a compatible package release. |
To change a locked selection, use lpm upgrade or update the applicable override, then review lpm.lock.
Deleting the lockfile changes the scope of resolution to the whole project.
Current lockfile version 13 uses authoritative TOML and does not produce a binary companion.
Advanced diagnostics
These environment variables help compare resolver behavior during troubleshooting:
| Variable | Effect |
|---|---|
LPM_RESOLVER=pubgrub | Select the alternative resolver. |
LPM_GREEDY_FUSION=0 | Keep greedy selection but disable fused metadata scheduling. |
LPM_NPM_FANOUT=<N> | Set the metadata concurrency limit. Defaults vary with install scheduling. |
LPM_WALKER=stream | Select streaming metadata traversal when the non-fused path runs. |
Higher metadata concurrency does not guarantee a faster install and can increase registry load. See Environment variables for diagnostic controls.
See also
- lpm install — apply project resolution settings and install the graph.
- lpm resolve — inspect candidate packages without installing them.
- lpm graph — inspect an existing project's dependencies.
- Lockfile — retain exact selections across installs.
- Save policy — choose the ranges written to the manifest.