jdx/mise · error

history cannot represent a non-UTF-8 filename; refusing to c

Error message

history cannot represent a non-UTF-8 filename; refusing to change its bytes

What it means

During the history walk, every captured file path is converted to a Rust str to be keyed into capture roots. If a filename is not valid UTF-8 (raw bytes on disk, e.g. from a non-UTF-8 locale filesystem), the history layer refuses to record it rather than silently corrupting or transcoding its bytes, so it aborts the walk.

Source

Thrown at src/system/history/tracked.rs:381

        // a hidden local-only history. Explicit encryption permits key files
        // to be tracked without storing their plaintext.
        walk.files.retain(|path, (_, policy)| {
            if let Some(reason) = capture_exclusion(path, policy) {
                walk.omitted.push(PathReason {
                    path: display_path(path),
                    reason: reason.into(),
                });
                false
            } else {
                true
            }
        });
        walk.entries = set.entries.clone();
        let config = normalize(&global_config_dir());
        let mut roots: BTreeMap<String, CaptureRoot> = BTreeMap::new();
        for (path, (owner, _)) in &walk.files {
            if path.to_str().is_none() {
                eyre::bail!(
                    "history cannot represent a non-UTF-8 filename; refusing to change its bytes"
                );
            }
            let (label, base, relative) = if let Ok(relative) = path.strip_prefix(&config) {
                ("config", config.clone(), relative.to_path_buf())
            } else if let Ok(relative) = path.strip_prefix(&home) {
                ("home", home.clone(), relative.to_path_buf())
            } else {
                (
                    "fs",
                    PathBuf::from(std::path::MAIN_SEPARATOR.to_string()),
                    path.components()
                        .filter(|c| matches!(c, Component::Normal(_)))
                        .collect(),
                )
            };
            let label = walk.entries[*owner]
                .variant

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Find and rename the offending non-UTF-8 filename to a UTF-8 name (e.g. with `convmv --notest -f latin1 -t utf8 -r .` or `find . | LC_ALL=C grep -P '[^\x00-\x7F]'` to locate it).
  2. Move the non-UTF-8 file outside of the tracked roots (home or the mise config directory) so the walk no longer encounters it.
  3. Delete the offending file if it is no longer needed.
  4. If you cannot fix the filename, file an issue to request a skip-with-warning mode instead of a hard abort.

Example fix

// before: file named 'r\e9sum\e9.txt' stored as Latin-1 bytes in a tracked directory
// after
convmv -f latin1 -t utf8 --notest 'r\e9sum\e9.txt'  # now valid UTF-8: 'résumé.txt'
Defensive patterns

Strategy: validation

Validate before calling

// before enabling history tracking over a directory, scan for non-UTF-8 names
use std::path::Path;
fn has_non_utf8_names(root: &Path) -> std::io::Result<bool> {
    let mut bad = false;
    for entry in walkdir_like(root) {
        if entry.path().to_str().is_none() {
            bad = true;
            eprintln!("non-UTF-8 filename: {:?}", entry.path());
        }
    }
    Ok(bad)
}

Type guard

fn is_utf8_path(p: &std::path::Path) -> bool { p.to_str().is_some() }

Try / catch

match result {
    Err(e) if e.to_string().contains("non-UTF-8 filename") => {
        eprintln!("rename non-UTF-8 files under tracked roots before tracking");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Running history tracking (walk, invoked via attempt_locked/live_tree/unsaved_paths) over a directory containing a file or directory whose name is not valid UTF-8, e.g. created by a tool using raw bytes or a legacy encoding.

Common situations: Filenames created on Linux with non-UTF-8 locales (Latin-1 bytes, GBK, etc.), files unpacked from old archives, or paths produced by legacy scripts; user then enables mise history tracking over their home directory and the walk hits the bad name.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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