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

  1. Track the real path (resolve the symlink with `readlink -f` / `realpath`) and pass that instead.
  2. Explicitly track the symlink itself AND its real target as separate tracking requests, as the message suggests.
  3. Restructure your directory layout so tracked files live under real (non-symlink) directories inside home or the config dir.
  4. 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

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


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/b5fbb2e70b3dab11. Report an issue: GitHub.