Schniz/fnm · error · anyhow::Error

Can't join paths: {source}

Error message

Can't join paths: {source}

What it means

After splitting the existing PATH and inserting fnm's bin dir at the front, `Shell::path` for PowerShell rebuilds one PATH string with `std::env::join_paths`. That function fails when any single entry contains the platform's path separator character itself (`;` on Windows, `:` on Unix), because the separator cannot appear unescaped inside an element. The io::Error is wrapped into this anyhow error.

Source

Thrown at src/shell/powershell.rs:17

use crate::version_file_strategy::VersionFileStrategy;

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!(

View on GitHub (pinned to 86adc9676c)

Solutions

  1. Locate the offending entry: `($env:PATH -split ';') | Where-Object { $_ -match '\\|/' -and (Test-Path $_) -eq $false }` — inspect entries that look like two paths glued by `;`.
  2. Rebuild a clean PATH excluding the malformed element, or rename the folder so it has no `;`.
  3. On Unix check for `:` inside PATH entries: `echo "$PATH" | tr ':' '\n' | grep ':'` (anything surviving the split embeds a colon).
  4. Re-run `fnm env --shell power-shell` after fixing PATH.

Example fix

# before
$env:PATH = 'C:\tools\a;b'; + $env:PATH   # one entry containing ';'
fnm env --shell power-shell   # error: Can't join paths

# after
$env:PATH = 'C:\tools\a;C:\tools\b' + ';' + $env:PATH
fnm env --shell power-shell   # 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(|entry| !entry.as_os_str().to_string_lossy().contains(sep))
        })
        .unwrap_or(false)
}

Try / catch

match powershell.path(&dir) {
    Ok(out) => out,
    Err(e) if e.to_string().contains("join paths") =>
        panic!("a PATH entry embeds the path separator; clean $env:PATH first: {e}"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: `fnm env --shell power-shell` when the current PATH contains one malformed entry embedding `;` inside a directory name (e.g. `C:\tools\a;b` set without quoting), or on Unix an entry containing `:`.

Common situations: Windows installers appending unquoted paths with semicolons in folder names; hand-edited system PATH in the registry dropping quotes; WSL interop appending Windows paths into $PATH; scripts doing `$env:PATH += 'C:\a;b'`.

Related errors


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