{"record":{"id":"909c5e41cd76a27a","repo":"jdx/mise","slug":"destination-variants-require-an-explicit-source-unless-the","errorCode":null,"errorMessage":"destination variants require an explicit source unless the entry key is a relative source path","messagePattern":"destination variants require an explicit source unless the entry key is a relative source path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/files.rs","lineNumber":247,"sourceCode":"    } 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 => {\n                bail!(\"an implied source entry key must not contain '..'\");\n            }\n            std::path::Component::RootDir | std::path::Component::Prefix(_) => {\n                bail!(\"an implied source entry key must be relative\");\n            }\n        }\n    }\n    if relative.as_os_str().is_empty() {\n        bail!(\"an implied source entry key must not be empty\");","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/files.rs#L229-L265","documentation":"This error comes from `logical_source_path` in the dotfiles system. When a `[dotfiles]` entry overrides destination targets via `variants` but omits an explicit `source`, the entry key itself is treated as the source path. That only works if the key is a relative source path; an absolute key (after `~/` expansion) cannot be reinterpreted as a source, so the library refuses it.","triggerScenarios":"Declaring a dotfile entry whose key is an absolute path (e.g. `\"~/.gitconfig\"`) or `/absolute/path`, with `variants` that set per-destination targets, and no `source` field in the entry table.","commonSituations":"Users copy a dotfile entry that previously had `source = \"dotfiles/gitconfig\"`, delete the source field while adding a `variants` block to target multiple machines, or write the key as an absolute destination path assuming the source is inferred from somewhere else.","solutions":["Add an explicit `source = \"relative/path\"` to the entry table","Change the entry key to a relative source path (e.g. `\"dotfiles/gitconfig\" = { variants = [...] }`) so the source can be implied","Remove the `variants` block if per-destination targets are not needed"],"exampleFix":"// before\n\"~/.zshrc\" = { variants = [ { target = \"~/.zshrc\" }, { target = \"~/.config/zsh/rc\" } ] }\n// after\n\"~/.zshrc\" = { source = \"dotfiles/zshrc\", variants = [ { target = \"~/.zshrc\" }, { target = \"~/.config/zsh/rc\" } ] }","handlingStrategy":"validation","validationCode":"function hasExplicitSource(entry) {\n  return typeof entry === 'string' || (entry && typeof entry.source === 'string' && entry.source.length > 0);\n}\n// before relying on implied source: if (entry.variants && !hasExplicitSource(entry) && isAbsolute(entryKey)) throw ...","typeGuard":"const isRelativeSourceKey = (key: string) => !key.startsWith('/') && !key.startsWith('~') && !/^[A-Za-z]:[\\\\/]/.test(key);","tryCatchPattern":null,"preventionTips":["Always set an explicit `source` when using `variants`","Keep entry keys relative (source-style) or always pair absolute keys with `source`"],"tags":["config","dotfiles","path-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"}