jdx/mise · error

cannot store dependency sidecar for invalid version

Error message

cannot store dependency sidecar for invalid version {version}

What it means

`ToolVersionSource::prepare` stores a dependency sidecar under the tool's version directory. Before doing so it requires the version string to be a plain file name (`is_plain_file_name`) — no path separators or special characters — because the version is used to build the on-disk directory path. Otherwise it bails to avoid writing to unintended locations.

Solutions

  1. Pin the tool to a concrete, plain version (e.g. `node@22.1.0`) before lockfile generation
  2. Resolve channel/ref aliases (latest, lts/*, ref:*) to concrete versions first
  3. Update the plugin/backend to emit plain version names for installed versions
  4. If intentional, skip dependency sidecar generation for that tool

Example fix

# before
node = "lts/iron"   # cannot store sidecar
# after
node = "22.11.0"
Defensive patterns

Strategy: validation

Validate before calling

fn is_plain_version(v: &str) -> bool {
    !v.is_empty()
        && !v.contains('/')
        && !v.contains('\\')
        && !v.contains(':')
        && v != "." && v != ".."
}

Type guard

fn plain_version(v: &str) -> Option<&str> {
    (!v.is_empty()
        && !v.contains('/')
        && !v.contains('\\')
        && !v.contains(':')).then_some(v)
}

Prevention

When it happens

Trigger: Calling `prepare` (during lockfile dependency handling) with a tool version like `ref:main`, `path:../foo`, `latest`, `lts/hydrogen`, or a version containing `/` or other non-plain-file characters; also when the graph dir is outside the managed root so a fresh version-based dir must be derived.

Common situations: Locking tools installed from git refs, paths, or channel aliases rather than concrete released versions; custom/non-semver version strings from plugins; tools installed via `mise use node@lts/iron`.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at src/lockfile/graph.rs:344

            root: absolute(&sidecar_root(lockfile)),
            ..Default::default()
        }
    }
    pub(super) fn reserve<T: NativeGraph>(&mut self, graph: &GraphRef<T>) {
        if let Some(dir) = graph.dir().filter(|dir| dir.starts_with(&self.root)) {
            self.referenced.insert(dir.to_path_buf());
        }
    }
    pub(super) fn prepare<T: NativeGraph>(
        &mut self,
        graph: &GraphRef<T>,
        short: &str,
        version: &str,
        backend: Option<&str>,
        options: &std::collections::BTreeMap<String, String>,
    ) -> Result<GraphRef<T>> {
        if !crate::file::is_plain_file_name(version) {
            bail!("cannot store dependency sidecar for invalid version {version}");
        }
        let dir = if let Some(dir) = graph.dir().filter(|dir| dir.starts_with(&self.root)) {
            dir.to_path_buf()
        } else {
            let parent = self.root.join(crate::backend::tool_directory_name(short));
            let plain = parent.join(version);
            if !options.is_empty() || self.referenced.contains(&plain) {
                parent.join(format!("{version}~{}", variant_suffix(backend, options)))
            } else {
                plain
            }
        };
        self.referenced.insert(dir.clone());
        if matches!(graph, GraphRef::Sidecar { dir: source, .. } if *source == dir && source.is_dir())
        {
            return Ok(graph.clone());
        }
        let body = match graph.load() {

View on GitHub (pinned to 533346cc37)