jdx/mise · error

workspace provider {:?} attributed lockfile {:?} to unknown

Error message

workspace provider {:?} attributed lockfile {:?} to unknown project {id:?}

What it means

WorkspaceProjectGraph::affected_projects_for_lockfile() validates that each ID a provider returns is both correctly namespaced AND present in the graph's discovered projects. This variant fires when the namespace is right but the project itself was never discovered — the provider attributed a lockfile to one of its own projects that the graph does not contain.

Source

Thrown at src/task/workspace.rs:971

        let mut affected = BTreeSet::new();
        for provider in providers {
            let Some(projects) =
                provider.affected_projects_for_lockfile(lockfile_path, before, after, self)?
            else {
                continue;
            };
            recognized = true;
            let expected_prefix = format!("{}:", provider.id());
            for id in projects {
                if !id.as_str().starts_with(&expected_prefix) {
                    bail!(
                        "workspace provider {:?} attributed lockfile {:?} to foreign project {id:?}",
                        provider.id(),
                        lockfile_path
                    );
                }
                if !self.projects.contains_key(&id) {
                    bail!(
                        "workspace provider {:?} attributed lockfile {:?} to unknown project {id:?}",
                        provider.id(),
                        lockfile_path
                    );
                }
                affected.insert(id);
            }
        }
        Ok(recognized.then_some(affected))
    }

    /// Summarizes provider failures retained during lenient task discovery.
    pub(crate) fn provider_discovery_error(&self) -> Option<String> {
        (!self.provider_errors.is_empty()).then(|| {
            self.provider_errors
                .iter()
                .map(|(provider, error)| format!("{provider}: {error}"))
                .collect::<Vec<_>>()

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Make the provider only attribute projects it would itself discover (cross-check its own discover() result before returning)
  2. Rebuild the graph from the same workspace root and provider set used for attribution so discovery and attribution agree
  3. Strip lockfile-attributed IDs that do not resolve via graph.get() before merging them into lockfile_projects

Example fix

// before (inside a custom provider)
let mut ids = BTreeSet::new();
for name in lockfile_packages { ids.insert(name.parse()?); }
Ok(Some(ids))

// after
let discovered: BTreeSet<ProjectId> = self.discover(root)?.into_iter().map(|p| p.id).collect();
let ids = lockfile_packages.iter().filter_map(|n| n.parse().ok()).filter(|id| discovered.contains(id)).collect();
Ok(Some(ids))
Defensive patterns

Strategy: validation

Validate before calling

// inside a provider: only attribute projects you would discover
let discovered: BTreeSet<ProjectId> = self.discover(workspace_root)?.into_iter().map(|p| p.id).collect();
let ids: BTreeSet<ProjectId> = attributed.into_iter().filter(|id| discovered.contains(id)).collect();
Ok((!ids.is_empty()).then_some(ids))

Type guard

fn attribution_is_discoverable(provider: &dyn WorkspaceProvider, root: &Path, ids: &BTreeSet<ProjectId>) -> bool {
    let discovered: BTreeSet<ProjectId> = provider.discover(root).map(|ps| ps.into_iter().map(|p| p.id).collect()).unwrap_or_default();
    ids.iter().all(|id| discovered.contains(id))
}

Try / catch

match graph.affected_projects_for_lockfile(providers, path, before, after) {
    Ok(res) => res,
    Err(err) if err.to_string().contains("attributed lockfile") => {
        warn!("provider attribution out of sync: {err}");
        None
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: A provider's lockfile attribution reads the lockfile contents (e.g. package names recorded in a lockfile) and returns an ID for a package that discovery skipped: the member is excluded from workspace members, its manifest was filtered out, the project was removed via [monorepo.projects] override, or discovery ran in lenient mode and that provider partially failed.

Common situations: Lockfile still lists a package after it was removed from pnpm-workspace.yaml / Cargo members; monorepo shrink during a refactor; provider attribution logic reading stale lockfile entries ('before'/'after' snapshots spanning a membership change).

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/1fe4ea259d15cf71. Report an issue: GitHub.