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

  1. Upgrade (or downgrade) to the version that understands the recorded root label
  2. Inspect the stored history record and correct the root label to a known prefix (home/config/fs)
  3. 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

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


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