{"record":{"id":"ef09ac763eda069e","repo":"denisidoro/navi","slug":"absent-shell-binary","errorCode":null,"errorMessage":"absent shell binary","messagePattern":"absent shell binary","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/common/shell.rs","lineNumber":43,"sourceCode":"\nimpl ShellSpawnError {\n    pub fn new<SourceError, T>(command: T, source: SourceError) -> Self\n    where\n        SourceError: std::error::Error + Sync + Send + 'static,\n        T: Into<String>,\n    {\n        ShellSpawnError {\n            command: command.into(),\n            source: source.into(),\n        }\n    }\n}\n\npub fn out() -> Command {\n    let words_str = CONFIG.shell();\n    let mut words_vec = shellwords::split(&words_str).expect(\"empty shell command\");\n    let mut words = words_vec.iter_mut();\n    let first_cmd = words.next().expect(\"absent shell binary\");\n    let mut cmd = Command::new(first_cmd);\n    cmd.args(words);\n    let dash_c = if words_str.contains(\"cmd.exe\") { \"/c\" } else { \"-c\" };\n    cmd.arg(dash_c);\n    cmd\n}\n","sourceCodeStart":25,"sourceCodeEnd":50,"githubUrl":"https://github.com/denisidoro/navi/blob/f7330b9ad5bd95b7d1a3c96d00e0a77deb589147/src/common/shell.rs#L25-L50","documentation":"After successfully splitting the configured shell string into words, `shell::out()` takes the first word as the shell binary via `words.next().expect(\"absent shell binary\")`. This cannot fail when the split succeeded with ≥1 word, but if it did (defensive branch/changed code path), it panics with \"absent shell binary\".","triggerScenarios":"Practically unreachable with the current code, since [27]'s expect guarantees at least one word; would trigger only if the code between the two expects changed or the iterator was advanced before `next()`.","commonSituations":"Custom-patched builds; refactoring that drains `words` before taking the first element; confusion while debugging the twin panic \"empty shell command\".","solutions":["If you see this, check whether the shell config string splits into words — you most likely hit \"empty shell command\" instead ([27]) and should fix the config first","Verify you are running an unmodified build of the tool","Refactor to `let Some(first_cmd) = words.next() else { bail!(\"empty shell command\") }` so both cases report config problems cleanly","Set a proper `shell` value in your config"],"exampleFix":"// before\nlet first_cmd = words.next().expect(\"absent shell binary\");\n// after\nlet first_cmd = words\n    .next()\n    .ok_or_else(|| anyhow!(\"no shell binary found in `shell` config\"))?;","handlingStrategy":"validation","validationCode":"let words = shellwords::split(cfg.shell()).unwrap_or_default();\nif words.first().is_none() {\n    eprintln!(\"no shell binary token in config; fix `shell` setting\");\n}","typeGuard":"fn first_token_is_binary(cfg: &Config) -> bool {\n    shellwords::split(cfg.shell())\n        .ok()\n        .and_then(|w| w.first().cloned())\n        .map_or(false, |b| which(&b).is_ok())\n}","tryCatchPattern":"// unreachable in unmodified builds; handle the sibling \"empty shell command\" panic instead:\nstd::panic::catch_unwind(shell::out).unwrap_or_else(|_| {\n    eprintln!(\"shell config invalid\");\n    std::process::exit(1);\n})","preventionTips":["Confirm the first token of your shell config names a real binary on PATH","Rebuild from upstream source if you suspect a local patch altered shell::out","Use `which <first-token>` to sanity-check the config","Validate shellwords::split output at config load time"],"tags":["rust","panic","config","shell"],"backgroundTag":"missing-shell-config","analyzedSha":"f7330b9ad5bd95b7d1a3c96d00e0a77deb589147","analyzedAt":"2026-09-03T13:58:22.429Z","contentChangedAt":"2026-09-03T13:58:22.429Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}