nikivdev/code · error

fish shell required. Run: chsh -s {}

Error message

fish shell required. Run:
  chsh -s {}

What it means

`ensure_fish_shell` is a health/startup check that reads the `SHELL` environment variable and requires it to contain "fish". If not, it locates the fish binary with `which` and bails telling the user to switch their login shell via `chsh -s <path>`. The check verifies the user's configured login shell, not merely that fish exists.

Source

Thrown at src/health.rs:84

        println!(
            "ℹ️  run internal repo not detected at {}",
            run_internal.display()
        );
    }

    println!(
        "ℹ️  run shortcuts: f r <task>, f ri <task>, f rp <project> <task>, f rip <project> <task>"
    );

    Ok(())
}

fn ensure_fish_shell() -> Result<()> {
    let shell = env::var("SHELL").unwrap_or_default();
    if !shell.contains("fish") {
        let fish = which::which("fish")
            .context("fish is required; install it and ensure it is on PATH")?;
        bail!("fish shell required. Run:\n  chsh -s {}", fish.display());
    }
    Ok(())
}

fn ensure_fish_flow_init() -> Result<()> {
    if which::which("f").is_ok() {
        return Ok(());
    }

    let config_path = fish_config_path()?;
    let content = fs::read_to_string(&config_path).unwrap_or_default();
    if content.contains("# flow:start") {
        return Ok(());
    }

    println!(
        "⚠ flow fish integration missing in {}. Ensure the `f` binary is on PATH, then run: f shell-init fish",
        config_path.display()

View on GitHub (pinned to a747e741ae)

Solutions

  1. Change your login shell to fish as the message suggests: `chsh -s $(which fish)` (then re-login)
  2. For a one-off run, set the variable: `SHELL=$(which fish) f <command>`
  3. Verify the variable: `echo $SHELL` — it must point at a path containing "fish"
  4. If the binary path itself doesn't contain the substring 'fish' (custom install path), symlink or reinstall fish to a standard path like /usr/bin/fish

Example fix

# before
echo $SHELL   # /bin/bash
# after
chsh -s /usr/bin/fish
# re-login, then:
echo $SHELL   # /usr/bin/fish
Defensive patterns

Strategy: validation

Validate before calling

let shell = std::env::var("SHELL").unwrap_or_default();
if !shell.contains("fish") {
    eprintln!("this tool requires fish as your login shell; run: chsh -s $(which fish)");
    std::process::exit(1);
}

Type guard

fn is_fish_shell() -> bool {
    std::env::var("SHELL")
        .map(|s| s.contains("fish"))
        .unwrap_or(false)
}

Try / catch

match ensure_fish_shell() {
    Err(e) if e.to_string().contains("fish shell required") => {
        eprintln!("{e}");
        eprintln!("or run once with: SHELL=$(which fish) f <cmd>");
    }
    r => r?,
}

Prevention

When it happens

Trigger: Running the tool with `$SHELL` set to bash/zsh/sh (or unset, defaulting to empty string) even though fish is installed.

Common situations: Running from a non-interactive context (IDE terminal, cron, CI) where SHELL is inherited as /bin/bash; fresh machines where fish was installed but `chsh` was never run; SSH sessions with a different default shell; calling the tool via `bash -c` from a script.

Related errors


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/dea0fce0a0ae4faf. Report an issue: GitHub.