{"record":{"id":"d9ab89ac4bce21f7","repo":"denisidoro/navi","slug":"empty-shell-command","errorCode":null,"errorMessage":"empty shell command","messagePattern":"empty shell command","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/common/shell.rs","lineNumber":41,"sourceCode":"    source: anyhow::Error,\n}\n\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":23,"sourceCodeEnd":50,"githubUrl":"https://github.com/denisidoro/navi/blob/f7330b9ad5bd95b7d1a3c96d00e0a77deb589147/src/common/shell.rs#L23-L50","documentation":"`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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nlet mut words_vec = shellwords::split(&words_str).expect(\"empty shell command\");\n// after\nlet mut words_vec = shellwords::split(&words_str)\n    .expect(\"empty shell command\")\n    .ok_or_else(|| anyhow!(\"`shell` config is empty; set a valid shell (e.g. bash)\"))?","handlingStrategy":"validation","validationCode":"let shell = config.get(\"shell\").unwrap_or_default();\nif shell.split_whitespace().count() == 0 {\n    eprintln!(\"config error: `shell` must be a non-empty command (e.g. bash)\");\n    std::process::exit(1);\n}","typeGuard":"fn has_valid_shell(cfg: &Config) -> bool {\n    cfg.shell().trim().split_whitespace().next().is_some()\n}","tryCatchPattern":"match std::panic::catch_unwind(shell::out) {\n    Ok(cmd) => cmd,\n    Err(_) => { eprintln!(\"shell command empty; set `shell` in your config\"); std::process::exit(1); }\n}","preventionTips":["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"],"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"}