Schniz/fnm · error · anyhow::Error

Can't join paths: {err}

Error message

Can't join paths: {err}

What it means

WindowsCmd's `Shell::path` splits the existing PATH, inserts fnm's bin dir at index 0, and rejoins with `std::env::join_paths`. Rejoining fails when any single entry contains the Windows path separator `;` (or `:` on Unix) inside itself, and the io::Error is wrapped into this anyhow error.

Source

Thrown at src/shell/windows_cmd/mod.rs:19

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

#[derive(Debug)]
pub struct WindowsCmd;

impl Shell for WindowsCmd {
    fn to_clap_shell(&self) -> clap_complete::Shell {
        // TODO: move to Option
        panic!("Shell completion is not supported for Windows Command Prompt. Maybe try using PowerShell for a better experience?");
    }

    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(|err| anyhow::anyhow!("Can't join paths: {err}"))?;
        let new_path = new_path
            .to_str()
            .ok_or_else(|| anyhow::anyhow!("Can't convert path to string"))?;
        Ok(format!("SET PATH={new_path}"))
    }

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

    fn use_on_cd(&self, config: &crate::config::FnmConfig) -> anyhow::Result<String> {
        let path = config.base_dir_with_default().join("cd.cmd");
        create_cd_file_at(&path).map_err(|source| {
            anyhow::anyhow!(
                "Can't create cd.cmd file for use-on-cd at {}: {}",
                path.display(),
                source
            )

View on GitHub (pinned to 86adc9676c)

Solutions

  1. Print PATH raw (`echo %PATH%` or `reg query "HKCU\Environment" /v Path`) and spot entries that contain an internal `;`.
  2. Fix the entry: quote it in the registry (System Properties → Environment Variables) or rename the folder without `;`.
  3. Replace the malformed entry with two properly separated entries.
  4. Re-run `fnm env --shell cmd`.

Example fix

:: before
set PATH=C:\tools\a;b;%PATH%
fnm env --shell cmd   :: error: Can't join paths

:: after
set PATH=C:\tools\a;C:\tools\b;%PATH%
fnm env --shell cmd   :: ok
Defensive patterns

Strategy: validation

Validate before calling

fn path_is_joinable() -> bool {
    let sep = if cfg!(windows) { ';' } else { ':' };
    std::env::var_os("path")
        .map(|p| std::env::split_paths(&p).all(|e| !e.as_os_str().to_string_lossy().contains(sep)))
        .unwrap_or(false)
}

Try / catch

match cmd_shell.path(&dir) {
    Ok(out) => out,
    Err(e) if e.to_string().contains("join paths") =>
        eprintln!("malformed PATH entry containing ';' — clean it and retry: {e}"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: `fnm env --shell cmd` when one PATH entry embeds a literal `;` in a directory name — e.g. `set PATH=C:\tools\a;b;%PATH%` without quoting — making the element unencodable in a joined PATH.

Common situations: Batch scripts appending unquoted paths containing semicolons; folders literally named with `;` (legal on NTFS); registry PATH edits that lost quotes around `C:\Program Files`-style entries with extra semicolons; ported Unix scripts leaving `:` entries.

Related errors


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