{"record":{"id":"092148342e8e1b86","repo":"gitbutlerapp/gitbutler","slug":"shell-configuration-cannot-be-performed-without-a-connected","errorCode":null,"errorMessage":"Shell configuration cannot be performed without a connected terminal","messagePattern":"Shell configuration cannot be performed without a connected terminal","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-installer/src/shell.rs","lineNumber":180,"sourceCode":"                || (trimmed.contains(\"set\") && trimmed.contains(\"PATH\"))\n        });\n\n        let has_completions = contents.contains(\"but completions\");\n        Some(ShellConfig {\n            shell: ShellType::Fish,\n            config_path,\n            has_completions,\n            has_path,\n        })\n    }\n}\n\n/// Interactively add shell configurations for the user.\n///\n/// This is designed to run interactively and errors if [`ui::is_connected_to_terminal`] returns `false`.\npub(crate) fn configure_shell(home_dir: &Path) -> Result<()> {\n    if !ui::is_connected_to_terminal() {\n        anyhow::bail!(\"Shell configuration cannot be performed without a connected terminal\");\n    }\n\n    let detected_shell_configs: Vec<ShellConfig> =\n        [ShellType::Bash, ShellType::Zsh, ShellType::Fish]\n            .map(|config| config.detect_config(home_dir))\n            .into_iter()\n            .flatten()\n            .collect();\n    let num_detected_shells = detected_shell_configs.len();\n\n    let unconfigured_shells: Vec<ShellConfig> = detected_shell_configs\n        .into_iter()\n        .filter(|cfg| !cfg.is_fully_configured())\n        .collect();\n\n    for cfg in &unconfigured_shells {\n        ui::println_empty();\n        info(&format!(","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-installer/src/shell.rs#L162-L198","documentation":"configure_shell edits shell rc files interactively and is documented to require a connected terminal; it bails when ui::is_connected_to_terminal() is false. This prevents silently rewriting dotfiles or hanging on prompts in headless environments.","triggerScenarios":"run_installation_impl reaching the shell-configuration step without a TTY — installer run in CI, Docker, cron, piped output, or after interactive mode was skipped.","commonSituations":"Installing via automation scripts, containers without a PTY, remote SSH without a TTY allocation (`ssh host but install` instead of `ssh -t`), output piped to a log file.","solutions":["Run the installer in an interactive terminal so PATH edits can be confirmed","Skip shell configuration (configure manually by adding the bin dir to PATH yourself)","Use `ssh -t` or a PTY wrapper when installing over SSH","Pre-set PATH via environment/config in automation so interactive shell setup is unnecessary"],"exampleFix":"// before\nssh deploy@host \"but install\"\n// after\nssh -t deploy@host \"but install\"  # allocate a TTY\n# or manually: echo 'export PATH=\"$HOME/.bin:$PATH\"' >> ~/.bashrc","handlingStrategy":"fallback","validationCode":"if !std::io::stdin().is_terminal() {\n    // skip shell configuration; print manual PATH instructions instead\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"connected terminal\") => {\n        // print manual shell-config instructions and continue non-interactively\n    }\n    other => other?,\n}","preventionTips":["Run installers in a real terminal or PTY (ssh -t, script)","Skip interactive shell setup in CI and configure PATH manually","Check terminal availability before starting interactive install"],"tags":["terminal","tty","shell","configuration","cli"],"backgroundTag":"missing-env-var","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}