jdx/mise · error · eyre::Report

workspace {kind} ID cannot be empty

Error message

workspace {kind} ID cannot be empty

What it means

Thrown while building a ProjectId, mise's provider-namespaced workspace project identifier. ProjectId::new(provider, local_id) and ProjectId::from_str both call validate_id_part, which rejects an empty provider or empty local part before joining them as "provider:local". The {kind} in the message is either "provider" or "project" and tells you which half of the pair is empty.

Source

Thrown at src/task/workspace.rs:65

    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Display::fmt(&self.0, f)
    }
}

impl FromStr for ProjectId {
    type Err = eyre::Report;

    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.

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. Fix the identifier so both halves are non-empty, e.g. "cargo:" -> "cargo:core"
  2. If the id came from [monorepo.projects.<id>] depends / depends_add / depends_remove in mise.toml, re-check every dependency string for a missing local part after the colon
  3. If the id came from ecosystem metadata, open the source file (go.mod module line, Cargo.toml package name) and give it a real value
  4. If you implement a WorkspaceProvider, never pass an empty name to ProjectId::new

Example fix

# before (mise.toml)
[monorepo.projects."cargo:app"]
depends = ["cargo:"]

# after
[monorepo.projects."cargo:app"]
depends = ["cargo:core"]
Defensive patterns

Strategy: validation

Validate before calling

fn check_id(id: &str) -> Result<(), String> {
    let Some((provider, local)) = id.split_once(':') else {
        return Err(format!("{id:?} has no provider namespace"));
    };
    if provider.is_empty() || local.is_empty() {
        return Err(format!("{id:?} has an empty provider or local part"));
    }
    Ok(())
}

for dep in &override_depends { check_id(dep)?; }

Type guard

fn is_valid_project_id(value: &str) -> bool {
    match value.split_once(':') {
        Some((provider, local)) => !provider.is_empty() && !local.is_empty(),
        None => false,
    }
}

Try / catch

match id.parse::<ProjectId>() {
    Ok(parsed) => parsed,
    Err(report) if report.to_string().contains("cannot be empty") => {
        // config data issue: surface the offending id and continue with a default
        fallback_id()
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: ProjectId::from_str("cargo:") (split_once(':') yields an empty local_id) when parsing depends/depends_add/depends_remove entries from a [monorepo.projects] override in mise.toml; ProjectId::from_str(":foo") yields kind=provider; a provider calling ProjectId::new(self.id(), "") because ecosystem metadata (e.g. a go.mod module path) parsed to an empty string.

Common situations: A trailing colon left after editing a dependency id in mise.toml; a copied project id whose local part was accidentally deleted; a degenerate go.mod whose module directive unescapes to an empty path; hand-writing override keys without copying an existing discovered id.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/62dd66dd8fe9e127. Report an issue: GitHub.