{"record":{"id":"62dd66dd8fe9e127","repo":"jdx/mise","slug":"workspace-kind-id-cannot-be-empty","errorCode":null,"errorMessage":"workspace {kind} ID cannot be empty","messagePattern":"workspace (.+?) ID cannot be empty","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/task/workspace.rs","lineNumber":65,"sourceCode":"    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Display::fmt(&self.0, f)\n    }\n}\n\nimpl FromStr for ProjectId {\n    type Err = eyre::Report;\n\n    fn from_str(value: &str) -> Result<Self> {\n        let Some((provider, local_id)) = value.split_once(':') else {\n            bail!(\"workspace project ID {value:?} must include a provider namespace\");\n        };\n        Self::new(provider, local_id)\n    }\n}\n\nfn validate_id_part(kind: &str, value: &str) -> Result<()> {\n    if value.is_empty() {\n        bail!(\"workspace {kind} ID cannot be empty\");\n    }\n    if value.trim() != value || value.chars().any(char::is_control) {\n        bail!(\n            \"workspace {kind} ID {value:?} contains surrounding whitespace or control characters\"\n        );\n    }\n    if kind == \"provider\" && value.contains(':') {\n        bail!(\"workspace provider ID {value:?} cannot contain ':'\");\n    }\n    Ok(())\n}\n\n/// A project discovered from ecosystem-specific workspace metadata.\n#[derive(Clone, Debug, Eq, PartialEq, Serialize)]\npub(crate) struct WorkspaceProject {\n    /// Stable identity used by dependency edges and task scoping.\n    pub id: ProjectId,\n    /// Normalized path relative to the workspace root. `.` represents the root.","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/task/workspace.rs#L47-L83","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the identifier so both halves are non-empty, e.g. \"cargo:\" -> \"cargo:core\"","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","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","If you implement a WorkspaceProvider, never pass an empty name to ProjectId::new"],"exampleFix":"# before (mise.toml)\n[monorepo.projects.\"cargo:app\"]\ndepends = [\"cargo:\"]\n\n# after\n[monorepo.projects.\"cargo:app\"]\ndepends = [\"cargo:core\"]","handlingStrategy":"validation","validationCode":"fn check_id(id: &str) -> Result<(), String> {\n    let Some((provider, local)) = id.split_once(':') else {\n        return Err(format!(\"{id:?} has no provider namespace\"));\n    };\n    if provider.is_empty() || local.is_empty() {\n        return Err(format!(\"{id:?} has an empty provider or local part\"));\n    }\n    Ok(())\n}\n\nfor dep in &override_depends { check_id(dep)?; }","typeGuard":"fn is_valid_project_id(value: &str) -> bool {\n    match value.split_once(':') {\n        Some((provider, local)) => !provider.is_empty() && !local.is_empty(),\n        None => false,\n    }\n}","tryCatchPattern":"match id.parse::<ProjectId>() {\n    Ok(parsed) => parsed,\n    Err(report) if report.to_string().contains(\"cannot be empty\") => {\n        // config data issue: surface the offending id and continue with a default\n        fallback_id()\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Always write dependency ids as provider:local with both halves filled (\"cargo:core\", \"go:example.com/pkg\", \"node:app\")","Copy ids from actual discovery output instead of typing them","Lint [monorepo.projects] keys and every depends*/ list in CI with a split-on-colon check"],"tags":["workspace","project-id","validation","rust","mise"],"backgroundTag":"empty-required-field","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}