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
- Pin the tool to a concrete, plain version (e.g. `node@22.1.0`) before lockfile generation
- Resolve channel/ref aliases (latest, lts/*, ref:*) to concrete versions first
- Update the plugin/backend to emit plain version names for installed versions
- 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
- Pin tools to concrete released versions before generating lockfiles
- Resolve ref:/path:/lts/latest aliases to concrete versions first
- Avoid locking dependency sidecars for tools installed from git refs or paths
- Validate plugin-reported version strings before storing them
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
- @ is not in the lockfile hint
- Python dependency graphs require lockfile revision 2; run…
- unsupported lockfile version
- additional_artifacts must be an array in lockfile
- aube lockfile mapping keys must be strings
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)