gitbutlerapp/gitbutler · error · anyhow::Error
EditorExitedWithNonZeroStatus
EditorExitedWithNonZeroStatus
Error message
Editor exited with non-zero status
What it means
When but needs text (e.g. a commit message) it spawns the configured editor through a shell (so commands like 'code --wait' work) and waits for it. If the editor process exits non-zero, the operation aborts with Code::EditorExitedWithNonZeroStatus attached as context. Quitting vim with :cq is the idiomatic deliberate way to trigger this.
Source
Thrown at crates/but/src/tui/get_text.rs:120
if !initial_text.ends_with('\n') {
writeln!(&mut tempfile)?;
}
writeln!(&mut tempfile, "{REST_TEXT_MARKER}")?;
writeln!(&mut tempfile, "{rest_text}")?;
}
// The editor command is allowed to be a shell expression, e.g. "code --wait" is somewhat common.
// We need to execute within a shell to make sure we don't get "No such file or directory" errors.
let status = gix::command::prepare(editor_cmd)
.arg(tempfile.path())
.stdin(std::process::Stdio::inherit())
.stdout(std::process::Stdio::inherit())
.with_shell()
.spawn()?
.wait()?;
if !status.success() {
return Err(anyhow::anyhow!("Editor exited with non-zero status")
.context(Code::EditorExitedWithNonZeroStatus));
}
Ok(std::fs::read(&tempfile)
.context("failed to read contents of commit message file")?
.into())
}
/// Launch the built-in TUI editor.
fn from_builtin_editor(
filename_safe_intent: &str,
initial_text: &str,
rest_text: Option<&str>,
) -> Result<BString> {
// Determine editor mode based on the intent
let mode = if filename_safe_intent.contains("commit") {
super::editor::EditorMode::CommitMessage
} else if filename_safe_intent.contains("branch") {View on GitHub (pinned to caf1f223d3)
Solutions
- If you aborted on purpose with :cq, rerun the command and save the message normally
- Test the editor standalone ($EDITOR /tmp/t) and fix core.editor if it fails or returns instantly, e.g. `git config --global core.editor 'code --wait'`
- Interpret the status: 127 means the shell could not find the editor command
- Provide the message non-interactively (e.g. -m flag) so no editor is launched
Example fix
# before git config --global core.editor "code" # returns instantly -> failure # after git config --global core.editor "code --wait"
Defensive patterns
Strategy: try-catch
Validate before calling
use which::which;
let prog = editor_cmd.split_whitespace().next().unwrap_or_default();
if which(prog).is_err() {
return Err(anyhow!("editor '{prog}' not found on PATH"));
} Try / catch
Inspect the error chain for Code::EditorExitedWithNonZeroStatus and branch: treat it as a user abort (drop the operation quietly) versus a real editor failure (surface the editor command and exit status).
Prevention
- Configure blocking editors ('code --wait', 'vim') in core.editor
- Verify $EDITOR works from a terminal before scripting commits
- Offer non-interactive message input (-m) in automation
When it happens
Trigger: Quitting the editor with :cq to abort; the editor binary missing so the shell exits 127; GUI editors returning instantly because a blocking flag like --wait is absent; the editor crashing or being killed.
Common situations: Intentional aborts via :cq; core.editor/GIT_EDITOR misconfigured; VS Code without --wait; editor not on PATH in the environment but inherited.
Related errors
- command exited with status {}
- Failed to sign GPG: {stdout} {stderr}
- failed to determine remote for non remote-tracking branch {t
- Refusing to overwrite an existing user.name and user.email
- Visible worktree {name} was listed more than once
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/688533649e26f573.
Report an issue: GitHub.