gitbutlerapp/gitbutler · warning
Cannot write to ( )
Error message
Cannot write to {} ({}) What it means
In setup_shell_config (but-installer/src/shell.rs), when appending the 'but' shell integration lines to a detected shell config file fails (std::io error), the installer warns with the config path and a mapped reason (permission denied / file not found / other) and then prints manual instructions so the user can add the lines themselves. Installation continues; only the automatic config step failed.
Solutions
- Follow the printed manual instructions: copy the shown lines into your shell config file by hand.
- Fix permissions on the config file/directory (`chown $USER ~/.zshrc; chmod u+w ~/.zshrc`) and re-run the installer.
- Create the missing config file if it does not exist (`touch ~/.zshrc`) and re-run.
- Check for immutable/managed dotfiles (`lsattr`, `chattr -i`) and unlock if applicable.
Example fix
# before $ ./but-installer # "Cannot write to /home/me/.zshrc (permission denied)" # after $ ls -l ~/.zshrc # owned by root? $ sudo chown me:me ~/.zshrc && chmod u+w ~/.zshrc $ ./but-installer
Defensive patterns
Strategy: try-catch
Validate before calling
# Pre-check writability before running the installer. test -w ~/.zshrc || echo 'config not writable: fix ownership/permissions first' [ -f ~/.zshrc ] || touch ~/.zshrc
Try / catch
try {
fs.appendFileSync(configPath, integrationLines);
} catch (e) {
console.warn(`Cannot write to ${configPath} (${e.code ?? e.message}); add these lines manually:`);
console.log(integrationLines);
} Prevention
- Run the installer as the same user who owns the dotfiles.
- Keep home directories writable (avoid read-only NFS homes for dev accounts).
- Create expected config files (touch ~/.zshrc) before installing on fresh machines.
- Unset the immutable bit on managed dotfiles before installers append to them.
When it happens
Trigger: setup_shell_config (called by setup_config) attempts to write to the detected config path (e.g. ~/.bashrc, ~/.zshrc) and the write returns Err - typically ErrorKind::PermissionDenied or NotFound.
Common situations: Running the installer as a different user than the config owner; read-only home directory or NFS-mounted home; config file missing and directory creation also failing; enterprise-managed machines with immutable dotfiles.
Understand the failure class
Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.
Related errors
- Could not detect your shell configuration file
- {err}
- failed to create config dir
- failed to create logs dir
- Failed to create symlink
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/91045d857cb70fad.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-installer/src/shell.rs:301
));
if needs_path {
ui::println_empty();
info("To use 'but' in this terminal session, you may need to run:");
ui::println(&format!(" source \"{}\"", cfg.config_path.display()));
ui::println_empty();
info("Or close and reopen your terminal");
}
}
Err(e) => {
use std::io::ErrorKind;
let error_msg = match e.kind() {
ErrorKind::PermissionDenied => "permission denied".to_string(),
ErrorKind::NotFound => "file not found".to_string(),
_ => format!("{e}"),
};
warn(&format!(
"Cannot write to {} ({})",
cfg.config_path.display(),
error_msg
));
info("Please add the following lines to your shell config file manually:");
if needs_path {
ui::println(&format!(" {path_cmd}"));
}
if needs_completions {
ui::println(&format!(" {completion_cmd}"));
}
}
}
Ok(())
}
View on GitHub (pinned to 58e5313667)