jdx/mise · error

workspace provider ID

Error message

workspace provider ID {value:?} cannot contain ':'

What it means

Because workspace project IDs are namespaced as `provider:local`, the provider part itself must not contain a colon, or the namespace would be ambiguous. `validate_id_part` enforces this only for `kind == "provider"` and fails with the offending value quoted.

Solutions

  1. Use a colon-free provider name (e.g. `npm` instead of `npm:`)
  2. Strip any namespace prefix before constructing the provider ID
  3. If the value came from a full project ID, split on `:` and use only the provider part

Example fix

// before
WorkspaceProject::new("npm:frontend", "web")
// after
WorkspaceProject::new("npm", "web")
Defensive patterns

Strategy: validation

Validate before calling

if provider.contains(':') {
    return Err(anyhow!("provider ID must not contain ':'"));
}
WorkspaceProject::new(provider, local_id)?;

Type guard

fn is_provider_id(s: &str) -> bool { !s.is_empty() && !s.contains(':') && s.trim() == s }

Try / catch

match WorkspaceProject::new(provider, local_id) {
    Ok(p) => p,
    Err(e) if e.to_string().contains("cannot contain ':'") => {
        let (prov, rest) = provider.split_once(':').unwrap();
        WorkspaceProject::new(prov, rest)?
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `WorkspaceProject::new("my:provider", local_id)` or registering a workspace provider whose `id()` contains `:`, e.g. copied from an already-namespaced project ID.

Common situations: A user pastes a full project ID like `npm:web` into a provider-name config field; a provider derives its ID from a URL or scoped package name containing `:`.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at src/task/workspace.rs:73

    fn from_str(value: &str) -> Result<Self> {
        let Some((provider, local_id)) = value.split_once(':') else {
            bail!("workspace project ID {value:?} must include a provider namespace");
        };
        Self::new(provider, local_id)
    }
}

fn validate_id_part(kind: &str, value: &str) -> Result<()> {
    if value.is_empty() {
        bail!("workspace {kind} ID cannot be empty");
    }
    if value.trim() != value || value.chars().any(char::is_control) {
        bail!(
            "workspace {kind} ID {value:?} contains surrounding whitespace or control characters"
        );
    }
    if kind == "provider" && value.contains(':') {
        bail!("workspace provider ID {value:?} cannot contain ':'");
    }
    Ok(())
}

/// A project discovered from ecosystem-specific workspace metadata.
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub(crate) struct WorkspaceProject {
    /// Stable identity used by dependency edges and task scoping.
    pub id: ProjectId,
    /// Normalized path relative to the workspace root. `.` represents the root.
    pub root: PathBuf,
    /// Provider-neutral facts that consumers may use for inspection or inference.
    pub metadata: BTreeMap<String, String>,
    /// Projects that this project directly depends on.
    pub dependencies: BTreeSet<ProjectId>,
    /// Provider and metadata source that inferred this project.
    pub provenance: WorkspaceProvenance,
    /// Provider attribution for each inferred dependency edge.

View on GitHub (pinned to 533346cc37)