{"record":{"id":"c80e6418916ba677","repo":"jdx/mise","slug":"destination-variants-require-an-explicit-source-when-any","errorCode":null,"errorMessage":"destination variants require an explicit source when any variant uses the entry key as its target","messagePattern":"destination variants require an explicit source when any variant uses the entry key as its target","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/files.rs","lineNumber":224,"sourceCode":"fn validate_file_variants(\n    target: &str,\n    source: Option<&str>,\n    content: Option<&str>,\n    mode: Option<&str>,\n    variants: &[FileVariant],\n) -> Result<Option<PathBuf>> {\n    let selectors: Vec<_> = variants.iter().map(|v| v.selector.clone()).collect();\n    crate::system::history::select::validate(&selectors)?;\n    let has_target_override = variants.iter().any(|v| v.target.is_some());\n    if has_target_override && mode == Some(\"track\") {\n        bail!(\"target overrides are not supported with mode = \\\"track\\\"\");\n    }\n    if has_target_override && content.is_some() {\n        bail!(\"destination variants with inline content are not supported\");\n    }\n    let implied_source = if has_target_override && source.is_none() {\n        if !variants.iter().all(|v| v.target.is_some()) {\n            bail!(\n                \"destination variants require an explicit source when any variant uses the entry key as its target\"\n            );\n        }\n        Some(logical_source_path(target)?)\n    } else {\n        None\n    };\n    if variants.is_empty() && resolve_target_arg(target).is_relative() {\n        bail!(\"target must be absolute or start with ~/\");\n    }\n    for variant in variants {\n        let destination = variant.target.as_deref().unwrap_or(target);\n        if !variant_target_is_absolute(destination) {\n            bail!(\"variant target must be absolute or start with ~/\");\n        }\n    }\n    Ok(implied_source)\n}","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/files.rs#L206-L242","documentation":"Thrown by `validate_file_variants` when destination variants are used but some variants omit `target`, and the entry has no explicit `source`. In that case the entry key itself becomes the implied target for variants that lack an override, and the tool would have to derive the source from that key via logical_source_path; to keep behavior explicit, the tool requires an explicit `source` whenever any variant uses the entry key as its target.","triggerScenarios":"Config with `has_target_override == true`, `source == None`, and at least one variant without `target` (so it falls back to the entry key as destination) — `!variants.iter().all(|v| v.target.is_some())` inside the implied-source branch.","commonSituations":"Adding one platform-specific variant with an override to a key-based entry that previously had no source and no variants; assuming all variants inherit the key path while partially specifying targets; refactoring entries and dropping the source field.","solutions":["Add an explicit `source` to the entry so the key-based variants have a well-defined source.","Or give every variant an explicit `target` so the key is not used as a target.","Or rename the entry key to the logical source path the tool would otherwise infer."],"exampleFix":"// before\n[[files]]\nkey = \"zshrc\"\n[[files.variants]]\ntarget = \"~/.config/zsh/.zshrc\"\n[[files.variants]]  # no target: uses key\n// after\n[[files]]\nkey = \"zshrc\"\nsource = \"zshrc\"\n[[files.variants]]\ntarget = \"~/.config/zsh/.zshrc\"\n[[files.variants]]","handlingStrategy":"validation","validationCode":"let has_override = entry.variants.iter().any(|v| v.target.is_some());\nlet all_overridden = entry.variants.iter().all(|v| v.target.is_some());\nif has_override && entry.source.is_none() && !all_overridden {\n    bail!(\"entry {} needs explicit source when some variants use the key as target\", entry.key);\n}","typeGuard":"fn needs_explicit_source(entry: &FileEntry) -> bool {\n    let any = entry.variants.iter().any(|v| v.target.is_some());\n    let all = entry.variants.iter().all(|v| v.target.is_some());\n    any && !all && entry.source.is_none()\n}","tryCatchPattern":null,"preventionTips":["Always set an explicit `source` when using destination variants.","Or override `target` on every variant consistently.","Lint configs so key-only entries never gain partial variants."],"tags":["config","validation","dotfiles"],"backgroundTag":"missing-required-config-field","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"}