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

  1. If you aborted on purpose with :cq, rerun the command and save the message normally
  2. 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'`
  3. Interpret the status: 127 means the shell could not find the editor command
  4. 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

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


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/688533649e26f573. Report an issue: GitHub.