jdx/mise · error

[dotfiles]."{req.target_raw}": target is not a safe OCI path

Error message

[dotfiles]."{req.target_raw}": target is not a safe OCI path

What it means

Before building a dotfiles OCI layer, each requested target path is normalized (absolute, forward slashes) and validated. This error is thrown when the resulting path is empty or any component is '..', because such paths would escape or be meaningless inside the image root and could enable path traversal in the OCI layer.

Source

Thrown at src/oci/builder.rs:1099

        (files, dirs)
    }
}

fn oci_target_path(req: &FileRequest) -> Result<String> {
    let raw = req.target_raw.as_str();
    let path = if raw == "~" {
        "root".to_string()
    } else if let Some(rest) = raw.strip_prefix("~/") {
        format!("root/{rest}")
    } else {
        req.target
            .strip_prefix("/")
            .map_err(|_| eyre::eyre!("dotfile target must be absolute: {}", req.target_raw))?
            .to_string_lossy()
            .replace('\\', "/")
    };
    if path.is_empty() || path.split('/').any(|p| p == "..") {
        bail!(
            "[dotfiles].\"{}\": target is not a safe OCI path",
            req.target_raw
        );
    }
    Ok(path)
}

fn source_mode(path: &std::path::Path) -> Result<u32> {
    let md = path.metadata()?;
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        Ok(md.permissions().mode() & 0o7777)
    }
    #[cfg(not(unix))]
    {
        let _ = md;
        Ok(0o644)

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Replace '..' components in the dotfile target with an absolute path rooted at '/' (or the correct home-relative location).
  2. Ensure the target is a non-empty absolute path, e.g. target = "/home/user/.zshrc" not "/" or "../x".
  3. If you intended a path outside the image via traversal, restructure the image so the file lives under a real prefix instead.

Example fix

// before
[[dotfiles]]
source = "zshrc"
target = "../.zshrc"
// after
[[dotfiles]]
source = "zshrc"
target = "/home/user/.zshrc"
Defensive patterns

Strategy: validation

Validate before calling

fn is_safe_oci_path(target: &str) -> bool {
    let path = target.strip_prefix('/').unwrap_or(target);
    !path.is_empty() && !path.split('/').any(|p| p == "..")
}
assert!(is_safe_oci_path("/home/user/.zshrc"));

Try / catch

match oci_target_path(&req) {
    Err(e) if e.to_string().contains("not a safe OCI path") => {
        eprintln!("rewrite target without '..' and as absolute path: {}", req.target_raw);
    }
    p => p,
}

Prevention

When it happens

Trigger: Calling build_dotfiles_layer with a req.target_raw that either becomes empty after stripping the leading '/' (e.g. target "/"), or contains a '..' component in any path segment (e.g. "~/../etc/passwd" or "/etc/../secret").

Common situations: Mistyped dotfile targets like "~/..", templated targets that expand to empty, copy-pasted relative paths such as "../dotfiles/zshrc", or targets using '..' to redirect outside the home directory.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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