GitoxideLabs/gitoxide · error
prepared commits have an editor
Error message
prepared commits have an editor
What it means
An `Option::expect` panic on `prepared.editor` in the `tix new` command. When no explicit `-m` message is given, `tix new` relies on `edit::create::prepare()` always providing an editor handle (`gix::command::Prepare`) so the commit message can be composed interactively; the `expect` asserts this invariant. Panicking means prepare regressed and returned `Prepared { editor: None }`, i.e. a bug in the library, not a user mistake.
Solutions
- Inspect `edit::create::prepare_inner` and ensure every return path sets `editor` when no explicit message was supplied.
- Make the missing editor a proper error instead of a panic: return a message telling the user to set `GIT_EDITOR`/`EDITOR`, or use the fallback editor path.
- Keep the explicit-message path separate (as it already is) so `prepare` is only required to spawn an editor on the interactive path.
- Reproduce with the `tix new` tests (`explicit_message_*`, `index_and_worktree_*`) to pin down which prepare path leaves the editor `None`.
Example fix
// before
let editor = prepared.editor.take().expect("prepared commits have an editor");
// after
let editor = prepared.editor.take().ok_or_else(|| {
anyhow::anyhow!("commit preparation did not provide an editor; set GIT_EDITOR or EDITOR")
})?; Defensive patterns
Strategy: validation
Validate before calling
let editor = match prepared.editor.take() {
Some(editor) => editor,
None => anyhow::bail!("commit preparation produced no editor; set GIT_EDITOR or EDITOR, or pass -m <message>"),
}; Type guard
fn has_editor(prepared: &crate::edit::create::Prepared) -> bool {
prepared.editor.is_some()
} Try / catch
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| run_new(&repository_path, bare, args))) {
Ok(result) => result,
Err(_panic) => anyhow::bail!("tix new panicked: prepared commit lacked an editor (library bug)"),
} Prevention
- Test the interactive `tix new` path (no -m) in environments with and without a terminal/editor
- Ensure prepare_inner never returns early without spawning the editor when no explicit message is given
- Use ok_or_else with an actionable message instead of expect for editor Option fields
- Fall back to a default editor (e.g. vi or GIT_EDITOR lookup) before failing
When it happens
Trigger: Running `tix new` without a message argument (interactive path) in a build where `edit::create::prepare`/`prepare_inner` skipped spawning the editor — e.g. after a refactor made editor setup conditional or failure silently non-fatal.
Common situations: Developer builds after changing how editors are spawned (missing `GIT_EDITOR`/`EDITOR` handling in prepare), CI sandboxes without a terminal where a new early-return path leaves `editor` unset, or patched builds that disable editor launch.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- prepared splits have an editor
- only value and unspecified are possible here
- parent-match assures this
- upper match already assured we only deal with blobs
- fixed size array with three items
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/7f349970fa3c243f.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/command/new.rs:61
.as_deref()
.map(gix::path::os_str_into_bstr)
.transpose()
.context("author is not valid UTF-8")?;
let repository_path = repository.git_dir().to_owned();
let bare = repository.is_bare();
let mut prepared = crate::edit::create::prepare_from(repository, parent, source, author, args.todo)?;
if prepared.is_empty && !args.allow_empty {
anyhow::bail!("the new commit would be empty; use --allow-empty to create it anyway");
}
let explicit = super::reword::explicit_message(&args.edit, std::io::stdin())?;
let outcome = if let Some(message) = explicit {
let mut repository = crate::open_repository(&repository_path, bare, false)
.context("could not reopen repository before creating commit")?;
repository.object_cache_size(None);
crate::edit::create::apply_message_reporting(repository, &graph, prepared, &message)?
} else {
let editor = prepared.editor.take().expect("prepared commits have an editor");
let Some(edited) = crate::edit::edit_document_without_terminal(
editor,
&prepared.document,
&format!("tix-commit-{}.md", std::process::id()),
)?
else {
println!("no commit created: no input was provided");
return Ok(());
};
let mut repository = crate::open_repository(&repository_path, bare, false)
.context("could not reopen repository after editing commit")?;
repository.object_cache_size(None);
crate::edit::create::apply_reporting(repository, &graph, prepared, &edited)?
};
let repository = crate::open_repository(&repository_path, bare, false)
.context("could not reopen repository after creating commit")?;
let selected = outcome
.selectedView on GitHub (pinned to e73179060b)