{"record":{"id":"6054fc20d530904f","repo":"gitbutlerapp/gitbutler","slug":"editor-exited-with-non-zero-status","errorCode":null,"errorMessage":"Editor exited with non-zero status","messagePattern":"Editor exited with non-zero status","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/but/src/command/legacy/reword2.rs","lineNumber":326,"sourceCode":"\nfn edit_commit_message(\n    repo: &gix::Repository,\n    context_lines: u32,\n    commit_details: CommitDetails,\n    editor_initial_message: &str,\n    current_message_for_comparison: &str,\n) -> anyhow::Result<Option<String>> {\n    get_commit_message_from_editor_legacy(\n        repo,\n        context_lines,\n        commit_details,\n        editor_initial_message.to_owned(),\n        current_message_for_comparison,\n        ShowDiffInEditor::Unspecified,\n    )\n    .map_err(|err| {\n        if let Some(Code::EditorExitedWithNonZeroStatus) = err.downcast_ref::<but_error::Code>() {\n            anyhow::anyhow!(\"Editor exited with non-zero status\")\n        } else {\n            err\n        }\n    })\n}\n\npub enum RewordOperation {\n    Commit {\n        target: CommitId,\n        new_message: CommitMessageSource,\n    },\n    FormatCommit {\n        target: CommitId,\n    },\n    Branch {\n        target: FullName,\n        new_name: BranchNameSource,\n    },","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but/src/command/legacy/reword2.rs#L308-L344","documentation":"Thrown by the legacy `but reword` flow when a commit message is edited in the user's Git editor. `get_commit_message_from_editor_legacy` launches the configured editor, and when the editor process exits with a non-zero status the underlying `but_error::Code::EditorExitedWithNonZeroStatus` is flattened into this plain anyhow message. A non-zero editor exit is the standard Git convention for deliberately aborting an edit (e.g. `:cq` in vim), so this usually means 'user cancelled', not corruption.","triggerScenarios":"Running the legacy reword command with the Editor message source (no `-m` supplied) while GIT_EDITOR / core.editor / EDITOR returns a non-zero exit code: quitting vim with `:cq`, a VS Code `--wait` wrapper exiting 1, an editor killed by a signal, or a broken editor binary.","commonSituations":"User intentionally aborts the message edit with `:cq`; EDITOR points to a wrapper script that always exits non-zero; running reword in CI or a non-TTY session where the editor fails immediately; CI treated the abort as a hard failure.","solutions":["If the abort was intentional, just rerun the command and either save the buffer or pass the message directly with the -m/--message flag","Test the configured editor outside `but`: `git var GIT_EDITOR` then launch that command by hand to confirm it exits 0 when saved","In scripts and CI, always pass -m \"new message\" so no editor is spawned at all","In wrappers, treat this specific error as a cancel (exit quietly) instead of surfacing a stack trace"],"exampleFix":"# before: opens an editor that may exit non-zero\nbut reword c3\n\n# after: non-interactive, no editor involved\nbut reword c3 -m \"fix: correct the commit message\"","handlingStrategy":"try-catch","validationCode":"let editor_spec = std::env::var(\"GIT_EDITOR\")\n    .or_else(|_| std::env::var(\"VISUAL\"))\n    .or_else(|_| std::env::var(\"EDITOR\"))\n    .unwrap_or_else(|_| \"vi\".to_string());\nlet bin = editor_spec.split_whitespace().next().unwrap_or(\"\");\nif which::which(bin).is_err() {\n    eprintln!(\"configured editor '{bin}' is not on PATH; pass -m or fix core.editor\");\n}","typeGuard":"fn is_editor_abort(err: &anyhow::Error) -> bool {\n    matches!(\n        err.downcast_ref::<but_error::Code>(),\n        Some(but_error::Code::EditorExitedWithNonZeroStatus)\n    ) || err.to_string() == \"Editor exited with non-zero status\"\n}","tryCatchPattern":"match run_reword_with_editor(&mut ctx) {\n    Ok(Some(message)) => { /* use message */ }\n    Ok(None) => { /* unchanged */ }\n    Err(err) if is_editor_abort(&err) => {\n        // user cancelled in the editor; not a failure\n        return Ok(());\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Always pass -m/--message in scripts and CI so no editor is spawned","Verify core.editor / GIT_EDITOR launches and exits 0 by hand after changing it","Treat editor non-zero exit as a cancel in wrappers, matching git's own convention","Remember `:cq` in vim is the deliberate abort gesture; use `:wq` to accept"],"tags":["rust","cli","git","editor","reword","user-abort"],"backgroundTag":"editor-exited-nonzero","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}