Schniz/fnm · error · anyhow::Error

Path is not valid UTF-8

Error message

Path is not valid UTF-8

What it means

fnm's Zsh shell integration renders `export PATH=<dir>:$PATH` from a `&Path` in `Shell::path`. `Path::to_str()` returns None when the path is not valid UTF-8, and that None becomes this anyhow error during shell-setup generation such as `fnm env --shell zsh` or `fnm use`.

Source

Thrown at src/shell/zsh.rs:18

use crate::version_file_strategy::VersionFileStrategy;

use super::shell::Shell;
use indoc::formatdoc;
use std::path::Path;

#[derive(Debug)]
pub struct Zsh;

impl Shell for Zsh {
    fn to_clap_shell(&self) -> clap_complete::Shell {
        clap_complete::Shell::Zsh
    }

    fn path(&self, path: &Path) -> anyhow::Result<String> {
        let path = path
            .to_str()
            .ok_or_else(|| anyhow::anyhow!("Path is not valid UTF-8"))?;
        let path =
            super::windows_compat::maybe_fix_windows_path(path).unwrap_or_else(|| path.to_string());
        Ok(format!("export PATH={path:?}:$PATH"))
    }

    fn set_env_var(&self, name: &str, value: &str) -> String {
        format!("export {name}={value:?}")
    }

    fn rehash(&self) -> Option<&'static str> {
        Some("rehash")
    }

    fn use_on_cd(&self, config: &crate::config::FnmConfig) -> anyhow::Result<String> {
        let version_file_exists_condition = if config.resolve_engines() {
            "-f .node-version || -f .nvmrc || -f package.json"
        } else {
            "-f .node-version || -f .nvmrc"

View on GitHub (pinned to 86adc9676c)

Solutions

  1. Validate candidate vars: `for v in FNM_DIR FNM_MULTISHELL_PATH XDG_RUNTIME_DIR XDG_CACHE_HOME; do printf '%s' "${(P)v}" | iconv -f UTF-8 -t UTF-8 >/dev/null || echo "$v: invalid UTF-8"; done`.
  2. Set clean values: `export FNM_DIR="$HOME/.local/share/fnm"; unset FNM_MULTISHELL_PATH` and restart the shell.
  3. Fix LANG/LC_ALL to a UTF-8 locale so system tools generate UTF-8 names.
  4. Remove stale multishell symlinks in the fnm multishells storage so fresh, valid ones are created.

Example fix

# before
export FNM_DIR=$'/data/b\xffnm'
fnm env --shell zsh   # error: Path is not valid UTF-8

# after
unset FNM_DIR
export FNM_DIR="$HOME/.local/share/fnm"
fnm env --shell zsh   # export PATH=... emitted
Defensive patterns

Strategy: validation

Validate before calling

fn fnm_paths_are_utf8() -> bool {
    ["FNM_DIR", "FNM_MULTISHELL_PATH", "XDG_RUNTIME_DIR", "XDG_CACHE_HOME"]
        .iter()
        .filter_map(std::env::var_os)
        .all(|v| v.to_str().is_some())
}

Type guard

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

Try / catch

match zsh_shell.path(&multishell_dir) {
    Ok(line) => println!("{line}"),
    Err(e) => eprintln!("fnm env failed; FNM paths must be UTF-8: {e}"),
}

Prevention

When it happens

Trigger: Running `fnm env --shell zsh` (or any zsh PATH rendering) when the multishell path / base dir — FNM_MULTISHELL_PATH, FNM_DIR, or the XDG runtime/cache dir it derives from — contains invalid UTF-8 bytes.

Common situations: `.zshrc` exporting FNM_DIR with non-UTF-8 bytes (file saved in a legacy encoding); XDG_RUNTIME_DIR or XDG_CACHE_HOME pointing at a byte-mangled path; macOS usernames with legacy-encoded characters; leftover multishell symlinks with invalid names.

Related errors


AI-assisted analysis of Schniz/fnm@86adc9676c (2026-08-16). Data as JSON: /api/errors/3b3409ed2b8e3ff8. Report an issue: GitHub.