{"record":{"id":"0f3bbb833b1ea3a4","repo":"jdx/mise","slug":"workspace-kind-id-value-contains-surrounding","errorCode":null,"errorMessage":"workspace {kind} ID {value:?} contains surrounding whitespace or control characters","messagePattern":"workspace (.+?) ID (.+?) contains surrounding whitespace or control characters","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/task/workspace.rs","lineNumber":68,"sourceCode":"}\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.\n    pub root: PathBuf,\n    /// Provider-neutral facts that consumers may use for inspection or inference.\n    pub metadata: BTreeMap<String, String>,","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/task/workspace.rs#L50-L86","documentation":"validate_id_part rejects a provider or local project id whose trimmed form differs from itself (leading/trailing whitespace) or that contains any Unicode control character anywhere. It runs for both halves of a ProjectId during construction and parsing, so ids must be clean, single-line tokens.","triggerScenarios":"A [monorepo.projects] dependency entry like \" node:app\" or \"node:app\\t\" in mise.toml (TOML basic strings keep inner whitespace); a value copied with an embedded newline from a multiline string; a provider building an id from a go module path that itself contains a control character after unescaping.","commonSituations":"Copy-pasting ids with a stray leading/trailing space; editors adding trailing whitespace; diff/merge tools introducing a carriage return into a quoted string.","solutions":["Strip surrounding whitespace from the offending dependency string or override key in mise.toml","Re-type the id instead of pasting, or paste with whitespace-trimming enabled","If the id is provider-derived, fix the source metadata (the go.mod module path or Cargo package name contains the bad character)","Add a config lint in CI that rejects whitespace/control characters in project ids"],"exampleFix":"# before (mise.toml)\n[monorepo.projects.\"node:app\"]\ndepends_add = [\" node:lib \"]\n\n# after\n[monorepo.projects.\"node:app\"]\ndepends_add = [\"node:lib\"]","handlingStrategy":"validation","validationCode":"fn is_clean_id_part(value: &str) -> bool {\n    !value.is_empty()\n        && value.trim() == value\n        && !value.chars().any(char::is_control)\n}\n\nfn is_valid_project_id(value: &str) -> bool {\n    value.split_once(':')\n        .map(|(p, l)| is_clean_id_part(p) && is_clean_id_part(l))\n        .unwrap_or(false)\n}","typeGuard":"fn is_valid_project_id(value: &str) -> bool {\n    match value.split_once(':') {\n        Some((provider, local)) => {\n            [provider, local].iter().all(|part| {\n                !part.is_empty() && part.trim() == *part && !part.chars().any(char::is_control)\n            })\n        }\n        None => false,\n    }\n}","tryCatchPattern":"// treat as config data error: report the exact id and its byte-level content\nErr(report) if report.to_string().contains(\"whitespace or control\") => {\n    eprintln!(\"project id {id:?} needs trimming; got bytes {:?}\", id.as_bytes());\n    continue;\n}","preventionTips":["Trim copied ids before pasting into mise.toml lists","Avoid multiline TOML strings for ids; keep each id on one line","Enable editor trim-trailing-whitespace on save for config files"],"tags":["workspace","project-id","whitespace","validation","rust","mise"],"backgroundTag":"invalid-identifier-format","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}