GitoxideLabs/gitoxide · error

prepared splits have an editor

Error message

prepared splits have an editor

What it means

This is a Rust `Option::expect` panic on `prepared.editor`, an internal invariant assertion in the `tix split` command. `crate::edit::split::prepare()` is written to always populate the optional editor handle (`gix::command::Prepare`) used to launch the user's editor for the split document; if it is `None`, the codebase's assumptions are broken. It is not a user-facing error — it panics because a developer error or regression made the editor unavailable.

Solutions

  1. Check the `split::prepare` implementation to confirm it always sets `editor`; fix any path that returns `Prepared` without an editor.
  2. If a no-editor mode is intentional, replace the `expect` with a proper match that returns a user-facing error or edits without launching an editor.
  3. Run `just test` / the split command tests to confirm prepare's contract before relying on it.
  4. Report the panic upstream with the exact command and environment that triggered it, since it indicates a bug in the prepare/editors pipeline.

Example fix

// before
let editor = prepared.editor.take().expect("prepared splits have an editor");
// after
let editor = prepared.editor.take().ok_or_else(|| {
    anyhow::anyhow!("split preparation did not provide an editor; check EDITOR/GIT_EDITOR settings")
})?;
Defensive patterns

Strategy: validation

Validate before calling

let editor = match prepared.editor.take() {
    Some(editor) => editor,
    None => anyhow::bail!("split preparation produced no editor; cannot open the split document"),
};

Type guard

fn has_editor(prepared: &crate::edit::split::Prepared) -> bool {
    prepared.editor.is_some()
}

Try / catch

match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| run_split(repository, graph, args))) {
    Ok(result) => result,
    Err(_panic) => anyhow::bail!("tix split panicked: prepared split lacked an editor (library bug)"),
}

Prevention

When it happens

Trigger: Running `tix split` where `edit::split::prepare()` returned a `Prepared` struct whose `editor` field was `None`. This can only happen if the prepare implementation changed to conditionally spawn an editor (e.g. skipping editor setup when the document is empty, or when stdin/stdout are not a terminal) without updating this call site.

Common situations: Hitting this during development of gix-tix after refactoring `split::prepare` or its `prepare_inner` helpers; running in environments where editor spawning fails earlier in prepare, leaving the handle unset; using a modified/patched build where the editor is deliberately suppressed.

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


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/ada3c77f59f3aa62. Report an issue: GitHub.

Appendix: source

Thrown at gix-tix/src/command.rs:681

        }
        if !seen.insert(path.clone()) {
            continue;
        }
        let change = changes
            .paths
            .iter()
            .find(|change| change.path == path)
            .with_context(|| format!("path {display:?} is not changed by HEAD"))?;
        selected.push(change.clone());
    }
    Ok(Some(selected))
}

fn split(repository: gix::Repository, graph: &crate::history::HistoryGraph, args: Split) -> Result<()> {
    let repository_path = repository.git_dir().to_owned();
    let bare = repository.is_bare();
    let mut prepared = crate::edit::split::prepare(repository, args.todo)?;
    let editor = prepared.editor.take().expect("prepared splits have an editor");
    let Some(edited) = crate::edit::edit_document_without_terminal(
        editor,
        &prepared.document,
        &format!("tix-split-{}.md", std::process::id()),
    )?
    else {
        println!("no split performed: no input was provided");
        return Ok(());
    };
    let mut repository = crate::open_repository(&repository_path, bare, false)
        .context("could not reopen repository after editing split")?;
    repository.object_cache_size(None);
    let outcome = crate::edit::split::apply_reporting(repository, graph, prepared, &edited, |_| {})?;
    let output_repository =
        crate::open_repository(&repository_path, bare, false).context("could not reopen repository after splitting")?;
    let selected = outcome.selected.context("splitting did not produce a selection")?;
    println!("{}", crate::change_id::display(&output_repository, selected, 7)?);
    print_ref_rewrites(&output_repository, &outcome.ref_rewrites)?;

View on GitHub (pinned to e73179060b)