denisidoro/navi · critical
empty shell command
Error message
empty shell command
What it means
`shell::out()` builds the `Command` used for shell-outs (e.g. by `copy`) from the configured shell string (`CONFIG.shell()`). It splits the string with `shellwords::split` and panics with "empty shell command" if splitting yields no words — typically because the configured shell string is empty or unparseable to zero tokens.
Source
Thrown at src/common/shell.rs:41
source: anyhow::Error,
}
impl ShellSpawnError {
pub fn new<SourceError, T>(command: T, source: SourceError) -> Self
where
SourceError: std::error::Error + Sync + Send + 'static,
T: Into<String>,
{
ShellSpawnError {
command: command.into(),
source: source.into(),
}
}
}
pub fn out() -> Command {
let words_str = CONFIG.shell();
let mut words_vec = shellwords::split(&words_str).expect("empty shell command");
let mut words = words_vec.iter_mut();
let first_cmd = words.next().expect("absent shell binary");
let mut cmd = Command::new(first_cmd);
cmd.args(words);
let dash_c = if words_str.contains("cmd.exe") { "/c" } else { "-c" };
cmd.arg(dash_c);
cmd
}
View on GitHub (pinned to f7330b9ad5)
Solutions
- Set a valid shell in your config (e.g. `shell = "bash"` or the full `sh -c` style string your platform needs)
- Inspect the resolved config (`CONFIG.shell()`) — check the relevant environment/config file actually defines it
- Avoid whitespace-only or quote-only values for the shell setting
- Patch `out()` to return a `Result` with a clear config error instead of panicking
Example fix
// before
let mut words_vec = shellwords::split(&words_str).expect("empty shell command");
// after
let mut words_vec = shellwords::split(&words_str)
.expect("empty shell command")
.ok_or_else(|| anyhow!("`shell` config is empty; set a valid shell (e.g. bash)"))? Defensive patterns
Strategy: validation
Validate before calling
let shell = config.get("shell").unwrap_or_default();
if shell.split_whitespace().count() == 0 {
eprintln!("config error: `shell` must be a non-empty command (e.g. bash)");
std::process::exit(1);
} Type guard
fn has_valid_shell(cfg: &Config) -> bool {
cfg.shell().trim().split_whitespace().next().is_some()
} Try / catch
match std::panic::catch_unwind(shell::out) {
Ok(cmd) => cmd,
Err(_) => { eprintln!("shell command empty; set `shell` in your config"); std::process::exit(1); }
} Prevention
- Always set an explicit `shell` value in your config file
- After config edits, run a command that shells out to verify early
- Never set shell to whitespace or quotes only
- Validate config at load time: reject empty shell strings
When it happens
Trigger: Calling `shell::out()` (directly or via `copy`/other shell-out helpers) when `CONFIG.shell()` resolves to an empty string, or a string that shellwords reduces to zero tokens (only quotes/whitespace).
Common situations: Empty or blank `shell` entry in the tool's config file; config load defaulting shell to empty; a config migration dropping the shell key; quoting mistakes that shellwords strips entirely.
Related errors
- absent shell binary
- No variables received from finder
- Unable to process value
- Unable to get selection
- Unable to get query
AI-assisted analysis of denisidoro/navi@f7330b9ad5 (2026-09-03).
Data as JSON: /api/errors/d9ab89ac4bce21f7.
Report an issue: GitHub.