jdx/mise · error
unknown history path root: {other}
Error message
unknown history path root: {other} What it means
When reading stored history metadata, each tree root label's prefix (before '@') must map to a known filesystem root: 'home', 'config', or 'fs'. An unrecognized prefix means the stored record references a root label this build does not understand, so metadata cannot be resolved to real paths.
Source
Thrown at src/system/history/shadow.rs:715
if layout.locate(&file.path).path().is_none() {
continue;
}
let label = file.path.split('/').next().unwrap_or_default().to_string();
let root = roots.entry(label.clone()).or_insert_with(|| RootRecord {
label,
..Default::default()
});
root.files += 1;
root.bytes += file.size.unwrap_or_default();
}
record.tree.roots = roots.into_values().collect();
}
for root in &mut record.tree.roots {
root.path = match root.label.split('@').next().unwrap_or_default() {
"home" => crate::dirs::HOME.to_path_buf(),
"config" => super::tracked::global_config_dir(),
"fs" => PathBuf::from(std::path::MAIN_SEPARATOR.to_string()),
other => bail!("unknown history path root: {other}"),
};
}
Ok(record)
}
/// Recursive listing of a tree (or a path inside it).
pub(crate) fn ls_tree(&self, spec: &str) -> Result<Vec<TreeEntry>> {
let out = self
.git
.output(PlumbingCall::new(["ls-tree", "-r", "-l", "-z", spec]))?;
let mut entries = vec![];
for record in out.split(|byte| *byte == 0) {
if record.is_empty() {
continue;
}
let record = std::str::from_utf8(record).wrap_err(
"history cannot represent a non-UTF-8 filename; refusing to change its bytes",
)?;View on GitHub (pinned to afd2eddd3a)
Solutions
- Upgrade (or downgrade) to the version that understands the recorded root label
- Inspect the stored history record and correct the root label to a known prefix (home/config/fs)
- If the record is corrupt or from an incompatible version, remove/prune that history entry and re-capture
Example fix
// before (stored record) root.label = "projects@/srv/code" // after (supported root) root.label = "fs@/srv/code"
Defensive patterns
Strategy: fallback
Validate before calling
// Rust: validate root labels before use
const KNOWN: [&str; 3] = ["home", "config", "fs"];
for label in &record.tree.roots.iter().map(|r| r.label.as_str()) {
let prefix = label.split('@').next().unwrap_or_default();
anyhow::ensure!(KNOWN.contains(&prefix), "unsupported history root: {prefix}");
} Type guard
fn known_root(label: &str) -> Option<&str> {
matches!(label.split('@').next()?, "home" | "config" | "fs").then(|| label)
} Try / catch
match read_meta(&git, oid) {
Err(e) if e.to_string().contains("unknown history path root") => {
eprintln!("history written by an incompatible version; upgrade or prune this entry");
}
other => other?,
} Prevention
- Keep the tool version that wrote history records installed when reading them
- Do not hand-edit history metadata files
- Prune history records created by incompatible versions before switching
When it happens
Trigger: read_meta (via checkpoint_refs) loads a history record whose root.label prefix (text before '@') is not one of home/config/fs.
Common situations: History data written by a newer or older version with different root labels; hand-edited or corrupted history metadata; records moved between machines with divergent label conventions.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- history is disabled (history.enabled = false)
- encrypted tracked files require [history.encryption].recipie
- cached rustc output directory has unsupported entries
- {bin_name} is a mise bin however it is not currently active.
- [dotfiles]."{}": source does not exist: {}
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/bc8c397d5ff1e7c5.
Report an issue: GitHub.