{"record":{"id":"30e2467a79e5ec5e","repo":"jdx/mise","slug":"bootstrap-files-source-and-content-are-mut","errorCode":null,"errorMessage":"[bootstrap.files].\"{}\": source and content are mutually exclusive","messagePattern":"\\[bootstrap\\.files\\]\\.\"(.+?)\": source and content are mutually exclusive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/managed_files.rs","lineNumber":442,"sourceCode":"        );\n    }\n    Ok(())\n}\n\nimpl ManagedFileRequest {\n    fn from_toml(\n        root_config: &Config,\n        path: PathBuf,\n        config: ManagedFileTomlConfig,\n        base: &Path,\n        secrets: &super::secrets::SecretValues,\n    ) -> Result<Self> {\n        let owner = nonempty(\"owner\", config.owner)?;\n        let group = nonempty(\"group\", config.group)?;\n        let mode = parse_mode(config.mode.as_deref(), 0o644)?;\n        let mut content = match (config.source, config.content, config.state) {\n            (Some(_), Some(_), _) => {\n                bail!(\n                    \"[bootstrap.files].\\\"{}\\\": source and content are mutually exclusive\",\n                    path.display()\n                )\n            }\n            (Some(source), None, ManagedState::Present) => {\n                let source = Path::new(&source);\n                let source = if source.is_absolute() {\n                    source.to_path_buf()\n                } else {\n                    base.join(source)\n                };\n                Some(fs::read_to_string(&source).wrap_err_with(|| {\n                    format!(\n                        \"[bootstrap.files].\\\"{}\\\": failed to read source {}\",\n                        path.display(),\n                        source.display()\n                    )\n                })?)","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/managed_files.rs#L424-L460","documentation":"ManagedFileRequest::from_toml pattern-matches on (source, content, state) and the (Some, Some, _) arm bails: a `[bootstrap.files]` entry cannot declare both `source` (path to a file whose contents to copy, resolved relative to the config file when relative) and `content` (inline string) (managed_files.rs:447-452). mise cannot know which one wins, so it rejects the ambiguity at parse time, before any templating.","triggerScenarios":"A single file entry like `[bootstrap.files.\"/etc/motd\"]` with both `source = \"motd.tmpl\"` and `content = \"hello\"` in mise.toml. The check fires for any state, including absent (where the (Some,Some,_) arm precedes the absent-specific arm, so both-fields-plus-absent reports this message first).","commonSituations":"Iterating on a file's content inline (content = ...) and forgetting to delete an earlier source = ...; merging config snippets from docs/examples that each used a different mechanism; absent-state entries left with stale content/source keys after switching state.","solutions":["Keep exactly one of source or content: use source for shared/template files on disk, content for short inline strings","If the entry is state = \"absent\", remove both keys entirely (see error 610)","After editing, re-run the bootstrap plan to confirm the entry parses"],"exampleFix":"# before\n[bootstrap.files.\"/etc/motd\"]\nsource = \"motd.tmpl\"\ncontent = \"welcome\"\n\n# after\n[bootstrap.files.\"/etc/motd\"]\ncontent = \"welcome\"","handlingStrategy":"validation","validationCode":"for (path, f) in bootstrap_files(config) {\n    assert!(!(f.source.is_some() && f.content.is_some()),\n        \"{path}: source and content are mutually exclusive\");\n}","typeGuard":"// Config struct invariant, checked before handing off to mise\nfn is_valid_file_entry(f: &ManagedFileTomlConfig) -> bool {\n    !(f.source.is_some() && f.content.is_some())\n}","tryCatchPattern":null,"preventionTips":["Pick one authoring style per entry (inline content OR source file) and stick to it","When toggling entries between content and source, replace rather than add","Lint TOML: any [bootstrap.files.*] table containing both keys is a CI failure"],"tags":["bootstrap","config-validation","mutually-exclusive","managed-files"],"backgroundTag":"mutually-exclusive-config-options","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}