jdx/mise · error
cannot track {} through symlinked parent {}; explicitly trac
Error message
cannot track {} through symlinked parent {}; explicitly track the link itself and its real target instead What it means
ensure_portable_ancestors walks every ancestor component of the requested path (excluding the final component) and rejects tracking if any ancestor is a symlink or junction. Watching/representing a path through a symlinked parent would capture events for the link's target under an unstable label, so mise asks you to track the link itself and its real target explicitly instead.
Source
Thrown at src/system/history/tracked.rs:838
Some(components.collect::<PathBuf>())
};
let Some((base, rest)) = bases
.iter()
.filter_map(|base| relative(base).map(|rest| (base, rest)))
.max_by_key(|(base, _)| base.components().count())
else {
eyre::bail!(
"tracking requires a portable path under home or the mise configuration directory"
);
};
let mut ancestor = base.clone();
for component in rest
.components()
.take(rest.components().count().saturating_sub(1))
{
ancestor.push(component);
if file::is_symlink_or_junction(&ancestor) {
eyre::bail!(
"cannot track {} through symlinked parent {}; explicitly track the link itself and its real target instead",
display_path(path),
display_path(&ancestor)
);
}
}
Ok(())
}
/// Turns a display or absolute path into its snapshot-tree path.
pub(crate) fn display_to_tree_path(path: &str) -> String {
// the link itself, never its destination: a tracked symlink is captured
// as a link and addressed as one
let expanded = normalize_target(Path::new(path));
let config = normalize(&global_config_dir());
if let Ok(relative) = expanded.strip_prefix(config) {
return format!("config/{}", relative.to_string_lossy().replace('\\', "/"))
.trim_end_matches('/')View on GitHub (pinned to afd2eddd3a)
Solutions
- Track the real path (resolve the symlink with `readlink -f` / `realpath`) and pass that instead.
- Explicitly track the symlink itself AND its real target as separate tracking requests, as the message suggests.
- Restructure your directory layout so tracked files live under real (non-symlink) directories inside home or the config dir.
- On macOS, replace `/tmp/...` paths with `/private/tmp/...`.
Example fix
// before mise history track ~/dev/tools/config.toml # ~/dev is a symlink to ~/code // after mise history track "$(realpath ~/dev)/tools/config.toml"
Defensive patterns
Strategy: validation
Validate before calling
// before tracking, ensure no ancestor is a symlink
use std::path::Path;
fn ancestors_are_real(path: &Path) -> std::io::Result<bool> {
let mut is_link = false;
for anc in path.ancestors().skip(1) {
if anc.symlink_metadata().map(|m| m.file_type().is_symlink()).unwrap_or(false) {
is_link = true;
eprintln!("symlinked ancestor: {:?}", anc);
}
}
Ok(!is_link)
} Try / catch
match result {
Err(e) if e.to_string().contains("symlinked parent") => {
eprintln!("use the real path (realpath) or track the link and target explicitly");
}
other => other?,
} Prevention
- Pass realpath-resolved paths to tracking APIs
- On macOS, prefer /private/tmp over /tmp
- Avoid nested symlinked directory layouts (Dropbox/iCloud redirections) for tracked roots
- Track symlink roots explicitly as links rather than paths beneath them
When it happens
Trigger: Calling add_requests with a path whose intermediate directories are symlinks, e.g. tracking `~/projects/link/src/file` where `~/projects/link` is a symlink to `~/code/real`; also triggered on macOS where /tmp -> /private/tmp makes `/tmp/...` paths fail.
Common situations: Tracking files under symlinked project dirs (`~/dev` -> `~/Documents/dev`), under `/tmp` on macOS, or inside Dropbox/OneDrive-style symlinked folders; common on macOS where home is symlinked (/Users vs /System/Volumes/Data).
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- tracking requires a portable path under home or the mise con
- remote cache symlink target must be relative
- remote cache symlink target is unsafe
- expected rustc output escapes its output directory
- [dotfiles]."{}": mode symlink-each requires a directory sour
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/b5fbb2e70b3dab11.
Report an issue: GitHub.