{"record":{"id":"e6a1cb75c78f9eef","repo":"jdx/mise","slug":"field-must-not-be-empty","errorCode":null,"errorMessage":"{field} must not be empty","messagePattern":"(.+?) must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/managed_files.rs","lineNumber":1217,"sourceCode":"    }\n    Ok(path)\n}\n\nfn parse_mode(mode: Option<&str>, default: u32) -> Result<u32> {\n    let Some(mode) = mode else {\n        return Ok(default);\n    };\n    let mode = mode.strip_prefix(\"0o\").unwrap_or(mode);\n    let parsed = u32::from_str_radix(mode, 8).wrap_err(\"mode must be an octal string\")?;\n    if parsed > 0o7777 {\n        bail!(\"mode must be between 0000 and 7777\");\n    }\n    Ok(parsed)\n}\n\nfn nonempty(field: &str, value: Option<String>) -> Result<Option<String>> {\n    match value {\n        Some(value) if value.trim().is_empty() => bail!(\"{field} must not be empty\"),\n        Some(value) => Ok(Some(value)),\n        None => Ok(None),\n    }\n}\n\nfn desired_metadata(kind: &str, mode: u32, owner: Option<&str>, group: Option<&str>) -> String {\n    let mut desired = format!(\"{kind} mode {mode:04o}\");\n    if let Some(owner) = owner {\n        desired.push_str(&format!(\" owner {owner}\"));\n    }\n    if let Some(group) = group {\n        desired.push_str(&format!(\" group {group}\"));\n    }\n    desired\n}\n\n#[cfg(unix)]\nfn metadata_matches(","sourceCodeStart":1199,"sourceCodeEnd":1235,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/managed_files.rs#L1199-L1235","documentation":"Optional string fields on managed files/directories (owner, group) pass through nonempty(): Some(value) whose trimmed form is empty is rejected, while an absent key (None) is fine. The {field} placeholder names the offending key in the message.","triggerScenarios":"owner = \"\" or group = \"   \" (key present but blank/whitespace-only) in a [bootstrap.files]/[bootstrap.directories] entry - typically from templating that renders an empty string instead of omitting the key.","commonSituations":"Config generated from templates with unset optional variables; mechanical YAML-to-TOML conversion leaving empty strings behind.","solutions":["Remove the owner/group key entirely if the attribute should not be managed","Set a real user/group name, e.g. owner = \"root\""],"exampleFix":"# before\n[bootstrap.directories]\n\"/opt/app\" = { owner = \"\", mode = \"0755\" }\n\n# after - omit the key\n[bootstrap.directories]\n\"/opt/app\" = { mode = \"0755\" }","handlingStrategy":"validation","validationCode":"if let Some(owner) = &entry.owner {\n    if owner.trim().is_empty() {\n        return Err(eyre::eyre!(\"owner must not be empty\"));\n    }\n}","typeGuard":"fn optional_field_is_valid(v: &Option<String>) -> bool {\n    v.as_ref().map(|s| !s.trim().is_empty()).unwrap_or(true)\n}","tryCatchPattern":null,"preventionTips":["Omit optional keys instead of setting empty strings","In templates, drop the key when the backing variable is unset"],"tags":["validation","config","empty-value","managed-files"],"backgroundTag":"empty-config-value","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}