pnpm/pnpm · error · InstallWithFreshLockfileError
Failed to read the manifest of a workspace root dependency:
Error message
Failed to read the manifest of a workspace root dependency: {_0} What it means
InstallWithFreshLockfileError::RootDepManifest wraps a PackageManifestError raised while reading the manifest of a workspace-root link:/file: dependency. Pacquet reads it because, under resolvePeersFromWorkspaceRoot, the root dependency's version can stand in for a peer it may satisfy; if that manifest cannot be read, the fresh install fails.
Source
Thrown at pnpm/crates/package-manager/src/install_with_fresh_lockfile.rs:484
LinkVirtualStoreBins(#[error(source)] LinkVirtualStoreBinsError),
/// The resolver chain failed for at least one dependency. The
/// diagnostic is forwarded transparently so a canonical inner code
/// (e.g. a traversal name's `ERR_PNPM_INVALID_DEPENDENCY_NAME`)
/// reaches the CLI unchanged. The `Display` still interpolates the
/// inner error so consumers that stringify the top-level error
/// (e.g. pnpr's `resolve.rs`, which forwards `err.to_string()` over
/// the wire) keep the detail.
#[display("Failed to resolve dependency tree: {_0}")]
#[diagnostic(transparent)]
ResolveDependencyTree(#[error(source)] ResolveDependencyTreeError),
/// Surfaces a failure to read the manifest of a workspace-root
/// `link:` / `file:` dependency, whose version stands in for the peer
/// it may satisfy under `resolvePeersFromWorkspaceRoot`.
#[display("Failed to read the manifest of a workspace root dependency: {_0}")]
#[diagnostic(transparent)]
RootDepManifest(#[error(source)] pnpm_package_manifest::PackageManifestError),
#[display("Failed to build lockfile from resolved dependency graph: {_0}")]
#[diagnostic(code(pnpm_package_manager::dependencies_graph_to_lockfile))]
DependenciesGraphToLockfile(#[error(source)] Box<DependenciesGraphToLockfileError>),
/// `minimumReleaseAgeExclude` patterns rejected at compile time.
/// Surfaced as `ERR_PNPM_INVALID_MINIMUM_RELEASE_AGE_EXCLUDE`.
#[display("Invalid value in minimumReleaseAgeExclude: {_0}")]
#[diagnostic(code(ERR_PNPM_INVALID_MINIMUM_RELEASE_AGE_EXCLUDE))]
MinimumReleaseAgeExclude(#[error(source)] pnpm_config::version_policy::VersionPolicyError),
/// `trustPolicyExclude` patterns rejected at compile time.
/// Surfaced as `ERR_PNPM_INVALID_TRUST_POLICY_EXCLUDE`.
#[display("Invalid value in trustPolicyExclude: {_0}")]
#[diagnostic(code(ERR_PNPM_INVALID_TRUST_POLICY_EXCLUDE))]
TrustPolicyExclude(#[error(source)] pnpm_config::version_policy::VersionPolicyError),
/// `allowBuilds` patterns in `pnpm-workspace.yaml` couldn't beView on GitHub (pinned to 6261b7f388)
Solutions
- Ensure every root link:/file: target contains a valid package.json with name and version
- Fix or remove stale link: entries that point at deleted or moved directories
- If peer satisfaction from the root is not needed, disable resolvePeersFromWorkspaceRoot
Example fix
# before (root package.json): "my-ui": "link:./packages/my-ui" but packages/my-ui/package.json is missing
# after: create packages/my-ui/package.json
{
"name": "my-ui",
"version": "1.0.0"
} Defensive patterns
Strategy: validation
Validate before calling
// Verify every root link:/file: target has a readable manifest before install
for (name, spec) in &root_manifest.dependencies {
if let Some(path) = spec.strip_prefix("link:").or_else(|| spec.strip_prefix("file:")) {
let manifest = std::path::Path::new(path).join("package.json");
let parsed: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(manifest)?)?;
assert!(parsed.get("name").is_some() && parsed.get("version").is_some());
}
} Type guard
fn is_root_dep_manifest_error(err: &InstallWithFreshLockfileError) -> bool {
matches!(err, InstallWithFreshLockfileError::RootDepManifest(_))
} Try / catch
match fresh_install().await {
Err(err @ InstallWithFreshLockfileError::RootDepManifest(_)) => {
// list root link:/file: deps and check each target for a valid package.json
}
result => result?,
} Prevention
- Initialize a package.json (name + version) in every locally linked package before linking it
- Update link: paths immediately when moving or renaming local packages
- Run a repo lint that resolves all link: targets to existing manifests
When it happens
Trigger: A root package.json dependency such as link:./local or file:./pkg whose target directory has no package.json, contains malformed JSON, or does not exist — with resolvePeersFromWorkspaceRoot in effect.
Common situations: Sibling workspace packages linked before being initialized (no package.json yet), moved or renamed local packages leaving stale link: entries, hand-written malformed manifests.
Related errors
- No package.json found in "${rootDir}"
- readPackage hook returned package manifest object's property
- CONFIG_SET_NOT_A_PROJECT_SETTING
- CONFIG_SET_UNSUPPORTED_WORKSPACE_KEY
- INVALID_PACKAGE_JSON
AI-assisted analysis of pnpm/pnpm@6261b7f388 (2026-08-17).
Data as JSON: /api/errors/636dcb3e5ebf70eb.
Report an issue: GitHub.