gitbutlerapp/gitbutler · critical

diffing-thread crashed

Error message

diffing-thread crashed

What it means

Branch listing details offload per-file line counting to a dedicated `gitbutler-diff-stats` thread fed over mpsc channels. The bail fires when `start.send(...)` fails, which can only happen once the receiving channel end in that thread was dropped — i.e. the diff-stats thread already exited, either because its initialization (`for_tree_diffing` / `diff_resource_cache_for_tree_diff`) failed or it panicked. The error therefore points at an earlier crash in the background worker, not at the send itself.

Source

Thrown at crates/gitbutler-branch-actions/src/branch.rs:843

                    }
                    Ok(())
                }
            })?;

        for branch in branches {
            let Some((base, authors, num_commits)) = merge_rx.recv()? else {
                continue;
            };

            let branch_head = branch.head;
            let base_commit = repo.find_object(base)?.try_into_commit()?;
            let base_tree = base_commit.tree()?;
            let head_tree = repo.find_object(branch_head)?.peel_to_tree()?;

            let ((change_tx, change_rx), (res_tx, rex_rx)) =
                (std::sync::mpsc::channel(), std::sync::mpsc::channel());
            if start.send((change_rx, res_tx)).is_err() {
                bail!("diffing-thread crashed");
            };
            base_tree
                .changes()?
                .options(|opts| {
                    opts.track_rewrites(None);
                })
                // NOTE: `stats(head_tree)` is also possible, but we have a separate thread for that.
                .for_each_to_obtain_tree(&head_tree, move |change| -> anyhow::Result<Action> {
                    change_tx.send(change.detach()).ok();
                    Ok(std::ops::ControlFlow::Continue(()))
                })?;
            let (number_of_files, lines_added, lines_removed) = rex_rx.recv()?;

            let branch_data = BranchListingDetails {
                name: branch.name,
                lines_added,
                lines_removed,
                number_of_files,

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Check application logs for a panic or error from the `gitbutler-diff-stats` thread — that is the real failure
  2. Run `git fsck` in the affected repository to rule out object database corruption
  3. Restart the app and retry; a transient init failure usually clears
  4. If it reproduces deterministically, capture the backtrace and report it as a GitButler bug
Defensive patterns

Strategy: retry

Try / catch

match list_branch_listing_details(ctx) {
    Ok(details) => { /* continue */ }
    Err(err) if err.to_string().contains("diffing-thread crashed") => {
        // the gitbutler-diff-stats worker died; check its panic log, restart, retry once
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: Calling the branch listing API while the `gitbutler-diff-stats` thread dies at startup (repository state its gix calls reject) or panics while handling a previous branch's diff, so subsequent `start.send` for the next branch fails.

Common situations: Corrupted packfiles or object database; repository mutated concurrently (git gc/prune, another Git client) while stats were computed; a GitButler bug panicking inside the diff-stats loop.

Related errors


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