{"record":{"id":"a0ee7693f119ad5b","repo":"nikivdev/code","slug":"absolute-paths-are-not-supported-in-sync-links","errorCode":null,"errorMessage":"absolute paths are not supported in sync links: {}","messagePattern":"absolute paths are not supported in sync links: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/home.rs","lineNumber":285,"sourceCode":"            format!(\n                \"failed to move {} to {}\",\n                dest.display(),\n                archive_path.display()\n            )\n        })?;\n        moved.push(archive_path);\n    }\n\n    Ok(moved)\n}\n\nfn normalize_dest_rel(dest: &Path) -> Result<PathBuf> {\n    let dest_str = dest.to_string_lossy();\n    if let Some(stripped) = dest_str.strip_prefix(\"~/\") {\n        return Ok(PathBuf::from(stripped));\n    }\n    if dest.is_absolute() {\n        bail!(\n            \"absolute paths are not supported in sync links: {}\",\n            dest.display()\n        );\n    }\n    Ok(dest.to_path_buf())\n}\n\nfn is_symlink_to(link: &Path, expected: &Path) -> bool {\n    let meta = match fs::symlink_metadata(link) {\n        Ok(v) => v,\n        Err(_) => return false,\n    };\n    if !meta.file_type().is_symlink() {\n        return false;\n    }\n\n    let target = match fs::read_link(link) {\n        Ok(v) => v,","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/home.rs#L267-L303","documentation":"normalize_dest_rel validates sync-link destination paths in the home dotfiles manager. Paths may be relative or use the `~/` prefix; any other absolute path (e.g. /etc/..., /home/...) is rejected because links are always created relative to the managed home directory. This keeps the sync manifest portable across machines and users.","triggerScenarios":"Calling archive_existing_configs, ensure_link_targets, or validate_setup with a dest entry that is neither `~/`-prefixed nor relative — i.e. dest.is_absolute() is true after the `~/` strip fails.","commonSituations":"Hand-edited home.toml sync entries with absolute paths copied from another machine; generating config from scripts that emit full paths; migrating configs between users where /home/alice/... was hardcoded.","solutions":["Rewrite the dest as a path relative to the home directory","Use the `~/` prefix form (e.g. `~/.config/foo`) which is stripped automatically","Remove the leading `/home/<user>` or `/Users/<user>` portion from the entry"],"exampleFix":"// before (home.toml)\n[[links]]\ndest = \"/home/alice/.vimrc\"\n// after\n[[links]]\ndest = \"~/.vimrc\"","handlingStrategy":"validation","validationCode":"fn is_valid_dest(dest: &str) -> bool {\n    dest.starts_with(\"~/\") || !std::path::Path::new(dest).is_absolute()\n}","typeGuard":"fn valid_dest_rel(dest: &Path) -> Option<PathBuf> {\n    let s = dest.to_string_lossy();\n    if let Some(r) = s.strip_prefix(\"~/\") { return Some(PathBuf::from(r)); }\n    if dest.is_absolute() { None } else { Some(dest.to_path_buf()) }\n}","tryCatchPattern":"match normalize_dest_rel(&dest) {\n    Ok(rel) => use(rel),\n    Err(e) if e.to_string().starts_with(\"absolute paths are not supported\") => {\n        eprintln!(\"rewrite '{}' relative to $HOME or as ~/-prefixed\", dest.display());\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always author sync-link dests as home-relative or ~/-prefixed paths","Keep home.toml machine-agnostic; never hardcode /home/<user>","Run validate_setup before applying links to catch bad entries early"],"tags":["path-validation","dotfiles","config"],"backgroundTag":"absolute-path-not-supported","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}