jdx/mise · error

workspace provider returned duplicate project ID

Error message

workspace provider {provider_id:?} returned duplicate project ID {id:?}

What it means

Projects are collected into a `BTreeMap` keyed by ID; inserting an ID that already exists means two projects collide, so the collector bails. Within one provider, a provider should never return the same project ID twice, and this check enforces that per provider.

Solutions

  1. Deduplicate discovered projects inside the provider before returning them
  2. Skip already-seen IDs when scanning (track a visited set by canonical path)
  3. Fix the provider scan so each project is emitted exactly once

Example fix

// before
for dir in dirs {
    discovered.push(make_project(dir));
}
// after
let mut seen = HashSet::new();
for dir in dirs {
    let p = make_project(dir);
    if seen.insert(p.id.clone()) {
        discovered.push(p);
    }
}
Defensive patterns

Strategy: validation

Validate before calling

let mut seen = std::collections::HashSet::new();
discovered.retain(|p| seen.insert(p.id.clone()));

Try / catch

match collect_projects(provider) {
    Ok(ps) => ps,
    Err(e) if e.to_string().contains("duplicate project ID") => {
        eprintln!("provider emitted the same project twice; dedupe before returning");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A single provider's `collect_provider_projects` returns two projects with identical `id` fields (e.g. the same project discovered via two metadata files, or a loop building duplicate entries).

Common situations: A provider scanning directories that matches the same package via both a lockfile and a manifest; symlinked directories visited twice; case-insensitive filesystems where two spellings collapse to one ID.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/048640f364ccd981. Report an issue: GitHub.

Appendix: source

Thrown at src/task/workspace.rs:664

                    provider_errors.insert(provider_id, error);
                    continue;
                }
                Err(error) => return Err(error),
            };
            for mut project in discovered {
                let expected_prefix = format!("{provider_id}:");
                let Some(local_id) = project.id.as_str().strip_prefix(&expected_prefix) else {
                    bail!(
                        "workspace provider {provider_id:?} returned project ID {:?}; IDs must use the {expected_prefix:?} namespace",
                        project.id
                    );
                };
                validate_id_part("project", local_id)?;
                project.root = normalize_project_root(&project.id, &project.root)?;
                attach_provider_provenance(&mut project, &provider_id);
                let id = project.id.clone();
                if projects.insert(id.clone(), project).is_some() {
                    bail!(
                        "workspace provider {provider_id:?} returned duplicate project ID {id:?}"
                    );
                }
            }
        }

        Ok(Self {
            projects,
            provider_errors,
        })
    }

    /// Applies explicit project and dependency changes after provider discovery.
    pub(crate) fn with_overrides(
        mut self,
        overrides: &BTreeMap<String, WorkspaceProjectOverride>,
    ) -> Result<Self> {
        let mut removed = BTreeSet::new();

View on GitHub (pinned to 533346cc37)