GitoxideLabs/gitoxide · error · anyhow::Error

history worker stopped unexpectedly

Error message

history worker stopped unexpectedly

What it means

The history worker thread that feeds events over an mpsc channel has terminated before sending all expected events. The main loop detects a disconnected channel while expecting messages and aborts instead of silently producing incomplete history output. This guards against a crashed/panicked worker silently corrupting results.

Solutions

  1. Check the worker thread for panics or early `?` returns that drop the sender without signalling history_finished.
  2. Ensure the worker sends a completion/terminal message before dropping the sender.
  3. Wrap the worker body so errors are forwarded through the channel (`message?` is already handled) instead of returning early.
  4. Re-run the command; if reproducible, capture a backtrace (RUST_BACKTRACE=1) and report the worker panic.

Example fix

// before
let handle = thread::spawn(move || {
    compute_history(tx)?; // early return drops tx silently
});
// after
let handle = thread::spawn(move || {
    let result = compute_history(&tx);
    let _ = tx.send(Event::Done(result.is_ok()));
    result
});
Defensive patterns

Strategy: try-catch

Try / catch

match result {
    Err(err) if err.to_string().contains("history worker stopped unexpectedly") => {
        // restart the operation or report worker crash
    }
    other => other?,
}

Prevention

When it happens

Trigger: The background history worker thread panicked or returned early (e.g. an internal Err was dropped, thread failed to spawn work) so the sender was dropped while the main loop still expected Event messages via receiver.try_recv().

Common situations: Worker thread panics on malformed repository data; history computation cancelled abnormally; a bug in the worker causing early return before channel closure protocol completes.

Related errors


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

Appendix: source

Thrown at gix-tix/src/lib.rs:1737

                fill_repository.retain = false;
                fill_repository.retained = None;
            }
            if quit_on_finish
                && quit_inputs.is_empty()
                && matches!(app.state, State::Complete)
                && lane_receiver.is_none()
            {
                return Ok(app.lane_time);
            }
            continue;
        }
        let mut events = 0;
        while !history_finished && events < EVENT_BATCH_SIZE {
            let message = match receiver.try_recv() {
                Ok(message) => message,
                Err(mpsc::TryRecvError::Empty) => break,
                Err(mpsc::TryRecvError::Disconnected) => {
                    anyhow::bail!("history worker stopped unexpectedly")
                }
            };
            events += 1;
            dirty = true;
            match message? {
                Event::Decorations(value) => {
                    app.set_worktree_head((!repository_is_bare).then(|| decoration_head(&value)).flatten(), true);
                    decorations = value;
                }
                Event::Commits(rows) => app.extend_commits(rows),
                Event::HiddenCommits(rows) => app.extend_hidden_commits(rows),
                Event::VisibleComplete => {
                    if let Some(rows) = app.start_lane_computation() {
                        lane_receiver = Some(start_lane_worker(rows));
                    }
                }
                Event::Complete(graph) => {
                    history_finished = true;

View on GitHub (pinned to e73179060b)