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 be

View on GitHub (pinned to 6261b7f388)

Solutions

  1. Ensure every root link:/file: target contains a valid package.json with name and version
  2. Fix or remove stale link: entries that point at deleted or moved directories
  3. 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

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


AI-assisted analysis of pnpm/pnpm@6261b7f388 (2026-08-17). Data as JSON: /api/errors/636dcb3e5ebf70eb. Report an issue: GitHub.