jdx/mise · error

variant target must be absolute or start with ~/

Error message

variant target must be absolute or start with ~/

What it means

Thrown by `validate_file_variants` when a variant's effective destination — its `target` override, or the entry's target when the variant has none — is not absolute (and does not start with `~/`). Every variant must resolve to an absolute destination; the check uses variant_target_is_absolute on the chosen destination.

Solutions

  1. Set each variant's `target` to an absolute path or one beginning with `~/`.
  2. If variants inherit the entry target, make the entry-level target absolute instead.
  3. Check any templated/interpolated target values for accidental emptiness or relative expansion.

Example fix

// before
[[files.variants]]
os = "linux"
target = "config/git/config"
// after
[[files.variants]]
os = "linux"
target = "~/.config/git/config"
Defensive patterns

Strategy: validation

Validate before calling

for v in &entry.variants {
    let dest = v.target.as_deref().unwrap_or(entry.target);
    if !dest.starts_with("~/") && !Path::new(dest).is_absolute() {
        bail!("variant target '{}' must be absolute or start with ~/", dest);
    }
}

Type guard

fn variant_dest_is_absolute(v: &FileVariant, entry_target: &str) -> bool {
    let dest = v.target.as_deref().unwrap_or(entry_target);
    dest.starts_with("~/") || Path::new(dest).is_absolute()
}

Prevention

When it happens

Trigger: Any `[[files.variants]]` whose `target` is a relative path, or which inherits a relative entry-level `target` — checked per-variant in the `for variant in variants` loop after the no-variant case.

Common situations: Adding variants for OS-specific configs and writing short relative destinations; a template/variable expanding to an empty or relative string; copying the source file name into the variant target field.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at src/system/files.rs:238

        bail!("destination variants with inline content are not supported");
    }
    let implied_source = if has_target_override && source.is_none() {
        if !variants.iter().all(|v| v.target.is_some()) {
            bail!(
                "destination variants require an explicit source when any variant uses the entry key as its target"
            );
        }
        Some(logical_source_path(target)?)
    } else {
        None
    };
    if variants.is_empty() && resolve_target_arg(target).is_relative() {
        bail!("target must be absolute or start with ~/");
    }
    for variant in variants {
        let destination = variant.target.as_deref().unwrap_or(target);
        if !variant_target_is_absolute(destination) {
            bail!("variant target must be absolute or start with ~/");
        }
    }
    Ok(implied_source)
}

fn logical_source_path(key: &str) -> Result<PathBuf> {
    let path = file::replace_path(key);
    if !path.is_relative() {
        bail!(
            "destination variants require an explicit source unless the entry key is a relative source path"
        );
    }
    let mut relative = PathBuf::new();
    for component in path.components() {
        match component {
            std::path::Component::Normal(component) => relative.push(component),
            std::path::Component::CurDir => {}
            std::path::Component::ParentDir => {

View on GitHub (pinned to 533346cc37)