{"record":{"id":"b83f8829c24c2c2d","repo":"jdx/mise","slug":"variant-target-must-be-absolute-or-start-with","errorCode":null,"errorMessage":"variant target must be absolute or start with ~/","messagePattern":"variant target must be absolute or start with ~/","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/files.rs","lineNumber":238,"sourceCode":"        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}\n\nfn logical_source_path(key: &str) -> Result<PathBuf> {\n    let path = file::replace_path(key);\n    if !path.is_relative() {\n        bail!(\n            \"destination variants require an explicit source unless the entry key is a relative source path\"\n        );\n    }\n    let mut relative = PathBuf::new();\n    for component in path.components() {\n        match component {\n            std::path::Component::Normal(component) => relative.push(component),\n            std::path::Component::CurDir => {}\n            std::path::Component::ParentDir => {","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/files.rs#L220-L256","documentation":"Thrown by `validate_file_variants` when a variant's effective destination — its `target` override, or the entry's target when the variant has none — is not absolute (and does not start with `~/`). Every variant must resolve to an absolute destination; the check uses variant_target_is_absolute on the chosen destination.","triggerScenarios":"Any `[[files.variants]]` whose `target` is a relative path, or which inherits a relative entry-level `target` — checked per-variant in the `for variant in variants` loop after the no-variant case.","commonSituations":"Adding variants for OS-specific configs and writing short relative destinations; a template/variable expanding to an empty or relative string; copying the source file name into the variant target field.","solutions":["Set each variant's `target` to an absolute path or one beginning with `~/`.","If variants inherit the entry target, make the entry-level target absolute instead.","Check any templated/interpolated target values for accidental emptiness or relative expansion."],"exampleFix":"// before\n[[files.variants]]\nos = \"linux\"\ntarget = \"config/git/config\"\n// after\n[[files.variants]]\nos = \"linux\"\ntarget = \"~/.config/git/config\"","handlingStrategy":"validation","validationCode":"for v in &entry.variants {\n    let dest = v.target.as_deref().unwrap_or(entry.target);\n    if !dest.starts_with(\"~/\") && !Path::new(dest).is_absolute() {\n        bail!(\"variant target '{}' must be absolute or start with ~/\", dest);\n    }\n}","typeGuard":"fn variant_dest_is_absolute(v: &FileVariant, entry_target: &str) -> bool {\n    let dest = v.target.as_deref().unwrap_or(entry_target);\n    dest.starts_with(\"~/\") || Path::new(dest).is_absolute()\n}","tryCatchPattern":null,"preventionTips":["Give every variant an absolute `~/`- or `/`-anchored target.","If variants inherit the entry target, ensure that target is absolute.","Check templated targets expand to non-empty absolute paths."],"tags":["config","paths","validation"],"backgroundTag":"invalid-config-value","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"}