Schniz/fnm · error · anyhow::Error

Can't read PATH

Error message

Can't read PATH

What it means

Once the new PATH is joined, PowerShell's `Shell::path` must render it inside a plain-string snippet (`$env:PATH = "..."`), so the joined OsString is converted with `to_str()`. If any pre-existing PATH entry holds bytes that are not valid UTF-8, the conversion returns None and this anyhow error fires.

Source

Thrown at src/shell/powershell.rs:20

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

#[derive(Debug)]
pub struct PowerShell;

impl Shell for PowerShell {
    fn path(&self, path: &Path) -> anyhow::Result<String> {
        let current_path =
            std::env::var_os("PATH").ok_or_else(|| anyhow::anyhow!("Can't read PATH env var"))?;
        let mut split_paths: Vec<_> = std::env::split_paths(&current_path).collect();
        split_paths.insert(0, path.to_path_buf());
        let new_path = std::env::join_paths(split_paths)
            .map_err(|source| anyhow::anyhow!("Can't join paths: {source}"))?;
        let new_path = new_path
            .to_str()
            .ok_or_else(|| anyhow::anyhow!("Can't read PATH"))?;
        Ok(self.set_env_var("PATH", new_path))
    }

    fn set_env_var(&self, name: &str, value: &str) -> String {
        format!(r#"$env:{name} = "{value}""#)
    }

    fn use_on_cd(&self, config: &crate::config::FnmConfig) -> anyhow::Result<String> {
        let version_file_exists_condition = if config.resolve_engines() {
            "(Test-Path .nvmrc) -Or (Test-Path .node-version) -Or (Test-Path package.json)"
        } else {
            "(Test-Path .nvmrc) -Or (Test-Path .node-version)"
        };
        let autoload_hook = match config.version_file_strategy() {
            VersionFileStrategy::Local => formatdoc!(
                r"
                    If ({version_file_exists_condition}) {{ & fnm use --silent-if-unchanged }}
                ",

View on GitHub (pinned to 86adc9676c)

Solutions

  1. Identify suspicious entries: iterate `($env:PATH -split ';')` and look for garbled/replacement characters.
  2. Remove or rename the offending entry (rename the folder to ASCII, or drop it from PATH via system settings).
  3. Re-set PATH from a known-good value: `[Environment]::SetEnvironmentVariable('Path', $clean, 'User')`.
  4. Ensure system locale / 'Beta: Use Unicode UTF-8 for worldwide language support' is consistent with how folders were named.

Example fix

# before
$env:PATH = "C:\Users\<garbled-ansi-name>\bin;" + $env:PATH
fnm env --shell power-shell   # error: Can't read PATH

# after
$env:PATH = "C:\Users\me\bin;" + $env:PATH
fnm env --shell power-shell   # ok
Defensive patterns

Strategy: validation

Validate before calling

fn path_entries_are_utf8() -> bool {
    std::env::var_os("PATH")
        .map(|p| std::env::split_paths(&p).all(|e| e.as_os_str().to_str().is_some()))
        .unwrap_or(false)
}

Type guard

fn is_utf8_os_string(s: &std::ffi::OsStr) -> bool {
    s.to_str().is_some()
}

Try / catch

if !path_entries_are_utf8() {
    eprintln!("PATH contains non-UTF-8 entries; fix them before running fnm env");
    std::process::exit(1);
}

Prevention

When it happens

Trigger: `fnm env --shell power-shell` when at least one existing PATH entry is non-UTF-8 — e.g. a directory created under an ANSI code page (Shift-JIS, Latin-1) on Windows, or a raw-byte path on Unix — so the joined string cannot be expressed as Rust `&str`.

Common situations: Non-English Windows profiles with legacy-encoded folder names in PATH (e.g. `C:\Users\< Shift-JIS name >\bin`); mojibake entries pasted from webpages; WSL paths with invalid bytes; PATH edited via tooling that wrote the registry value in the wrong encoding.

Related errors


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