{"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":null,"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/533346cc374382b41ec5ff70536252b2e96e725c/src/task/workspace.rs#L50-L86","documentation":"`validate_id_part` rejects workspace ID components that have leading/trailing whitespace or contain control characters. IDs are used in maps, prefixes like `provider:local`, and output, so untrimmed or invisible characters would break matching and lookups. The library rejects them up front with the offending value quoted.","triggerScenarios":"Calling `new` or a provider's `collect_provider_projects` with IDs containing spaces at the edges (e.g. `\" api \"`) or control chars such as `\\t`, `\\n`, `\\r` from untrimmed config values or split output.","commonSituations":"Hand-edited mise.toml with stray spaces after `=`; splitting provider metadata on a separator that leaves `\\r` (CRLF files); shell-interpolated values with trailing whitespace.","solutions":["Trim the ID before passing it (`.trim()`)","Fix the config file to remove stray spaces or control characters","Normalize line endings (CRLF -> LF) in files the ID is parsed from"],"exampleFix":"// before\nlet project = WorkspaceProject::new(provider, &raw_id);\n// after\nlet project = WorkspaceProject::new(provider, raw_id.trim());","handlingStrategy":"validation","validationCode":"let p = provider.trim();\nlet l = local_id.trim();\nif p != provider || l != local_id {\n    eprintln!(\"trimming workspace ID parts\");\n}\nWorkspaceProject::new(p, l)?;","typeGuard":"fn is_clean_id(s: &str) -> bool { s.trim() == s && !s.chars().any(char::is_control) }","tryCatchPattern":"match WorkspaceProject::new(provider, local_id) {\n    Ok(p) => p,\n    Err(e) if e.to_string().contains(\"surrounding whitespace\") => WorkspaceProject::new(provider.trim(), local_id.trim())?,\n    Err(e) => return Err(e),\n}","preventionTips":["Trim all strings read from config or environment before use","Normalize line endings (strip \\r) when parsing files on Windows","Add a unit test asserting IDs have no whitespace"],"tags":["workspace","validation","whitespace"],"backgroundTag":"invalid-identifier-format","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}