{"record":{"id":"bbceec5a1a3324c8","repo":"jdx/mise","slug":"an-implied-source-entry-key-must-not-be-empty","errorCode":null,"errorMessage":"an implied source entry key must not be empty","messagePattern":"an implied source entry key must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/files.rs","lineNumber":265,"sourceCode":"        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\");\n    }\n    Ok(relative)\n}\n\n/// Inactive variants can contain another platform's absolute path syntax.\n/// The selected destination is still checked with native path rules before use.\nfn variant_target_is_absolute(target: &str) -> bool {\n    let bytes = target.as_bytes();\n    resolve_target_arg(target).is_absolute()\n        || target.starts_with('/')\n        || target.starts_with(\"\\\\\\\\\")\n        || (bytes.len() >= 3\n            && bytes[0].is_ascii_alphabetic()\n            && bytes[1] == b':'\n            && matches!(bytes[2], b'/' | b'\\\\'))\n}\n\n/// One `[dotfiles]` whole-file entry as written in mise.toml.","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/files.rs#L247-L283","documentation":"If every component of the entry key is trivial (e.g. the key is `.`, `./`, or `~/` alone after expansion), the constructed relative source path is empty, which is not a valid file. `logical_source_path` rejects this rather than silently treating the dotfiles root as the source.","triggerScenarios":"Entry key that normalizes to nothing, such as `\".\"` or `\"~/\"`, combined with `variants` and no explicit `source`.","commonSituations":"Copy-paste mistakes leaving a placeholder key like `\".\"`, or trimming a key down while debugging and leaving it empty of real components.","solutions":["Replace the placeholder key with the actual relative source file path","Add an explicit `source` field naming the file","Remove the entry if it was accidental"],"exampleFix":"// before\n\".\" = { variants = [ { target = \"~/.zshrc\" } ] }\n// after\n\"~/.zshrc\" = { source = \"dotfiles/zshrc\", variants = [ { target = \"~/.zshrc\" } ] }","handlingStrategy":"validation","validationCode":"function keyNormalizesToEmpty(key) {\n  const rel = key.replace(/^~\\/?/, '').replace(/^[.\\\\/]+/, '');\n  return rel.length === 0;\n}","typeGuard":"const isNonEmptyRelativeKey = (key: string) => key.replace(/^[~.]\\/?/, '').split(/[\\\\/]/).filter(c => c && c !== '.').length > 0;","tryCatchPattern":null,"preventionTips":["Never use `.` or `~/` alone as an entry key","Ensure every key names a concrete file or directory"],"tags":["config","dotfiles","empty-value"],"backgroundTag":"empty-required-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"}