jdx/mise · error

invalid dependency sidecar path

Error message

invalid dependency sidecar path {}

What it means

Dependency sidecars record an external dependency graph per tool. `GraphRef::resolve_path` validates the sidecar's relative `dir`: it must be relative, contain only normal path components, and have no backslashes — otherwise the path could escape the tool directory or be non-portable. It then resolves the dir against the lockfile's parent directory.

Solutions

  1. Fix the `path` in the lockfile's dependency sidecar entry to a relative path with only normal components (e.g. `deps/tool`)
  2. Remove any `..`, absolute prefixes, or `\` separators from the path
  3. Regenerate the lockfile instead of hand-editing so the sidecar path is produced correctly
  4. If the graph lives elsewhere, move the sidecar directory under the tool directory and reference it relatively

Example fix

# before (mise.lock)
path = "../shared/graph.toml"
# after
path = "shared/graph.toml"
Defensive patterns

Strategy: validation

Validate before calling

use std::path::{Component, Path};
fn is_valid_sidecar_dir(dir: &Path) -> bool {
    !dir.is_absolute()
        && dir.components().all(|c| matches!(c, Component::Normal(_)))
        && !dir.to_string_lossy().contains('\\')
}

Type guard

fn valid_sidecar_dir(dir: &str) -> Option<&str> {
    let p = Path::new(dir);
    (!p.is_absolute()
        && p.components().all(|c| matches!(c, Component::Normal(_)))
        && !dir.contains('\\')).then_some(dir)
}

Prevention

When it happens

Trigger: Loading a lockfile whose `[dependency]` sidecar entry has an absolute path, `..`/root/curdir components (e.g. `../shared/deps`), or Windows-style backslashes in `path`, then calling `resolve_path`.

Common situations: Hand-edited or generated-by-script lockfiles with absolute or traversal paths; lockfiles copied from Windows; tools writing sidecar paths computed on another OS.

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@533346cc37 (2026-09-17). Data as JSON: /api/errors/5c98a22c79d7f3bb. Report an issue: GitHub.

Appendix: source

Thrown at src/lockfile/graph.rs:138

            return Ok(self.clone());
        };
        let text = std::fs::read_to_string(dir.join(T::GRAPH_FILE))
            .map_err(|e| eyre!("dependency sidecar {}: {e}; run `mise lock`", dir.display()))?;
        let graph = T::read(dir, text)
            .map_err(|e| eyre!("dependency sidecar {}: {e}; run `mise lock`", dir.display()))?;
        Ok(Self::Inline {
            graph,
            dir: Some(dir.clone()),
            digest: OnceLock::new(),
        })
    }
    pub(crate) fn resolve_path(&mut self, lockfile: &Path) -> Result<()> {
        if let Self::Sidecar { dir, .. } = self {
            if dir.is_absolute()
                || dir.components().any(|c| !matches!(c, Component::Normal(_)))
                || dir.to_string_lossy().contains('\\')
            {
                bail!("invalid dependency sidecar path {}", dir.display());
            }
            *dir = absolute(lockfile.parent().unwrap_or(Path::new("."))).join(&*dir);
        }
        Ok(())
    }
    pub(crate) fn parse(value: toml::Value) -> Result<Self> {
        if value.get("path").is_some() {
            #[derive(Deserialize)]
            #[serde(deny_unknown_fields)]
            struct Pointer {
                path: PathBuf,
                digest: String,
            }
            let p: Pointer = value.try_into()?;
            if !p
                .digest
                .strip_prefix("sha256:")
                .is_some_and(|s| s.len() == 64 && s.bytes().all(|b| b.is_ascii_hexdigit()))

View on GitHub (pinned to 533346cc37)