gitbutlerapp/gitbutler · error

handled above

Error message

handled above

What it means

During but-graph initialization, segment tips are matched on `TipRole`; `TipRole::TargetRemote` is marked `unreachable!("handled above")` because remote-target tips are selected and processed in an earlier phase of the same loop, so by the time this match runs no TargetRemote tips remain. Like the sibling 'handled above' markers, it is an ordering invariant: the panic fires only if the earlier TargetRemote handling is removed or made incomplete while this match still assumes it.

Source

Thrown at crates/but-graph/src/init/mod.rs:2227

                    (None, CommitFlags::empty()),
                    target_limit,
                    commit_graph,
                    repo,
                    buf,
                )?;
            }
            continue;
        }

        let (flags, limit) = match &tip.role {
            TipRole::Reachable if tip.is_entrypoint => {
                graph.entrypoint = Some((segment, EntryPointCommit::AtCommit(tip.id)));
                (entrypoint_flags, max_limit)
            }
            TipRole::Reachable => {
                reachable_tip_flags_and_limit(tip.id, entrypoint, max_limit, goals)
            }
            TipRole::TargetRemote => unreachable!("handled above"),
            TipRole::Workspace => {
                if tip.is_entrypoint && graph.entrypoint.is_none() {
                    graph.entrypoint = Some((segment, EntryPointCommit::AtCommit(tip.id)));
                }
                let extra_flags = if tip.is_entrypoint {
                    entrypoint_flags
                } else {
                    CommitFlags::empty()
                };
                let limit = if tip.is_entrypoint {
                    max_limit
                } else {
                    max_limit.with_indirect_goal(entrypoint, goals)
                };
                (
                    CommitFlags::InWorkspace | CommitFlags::NotInRemote | extra_flags,
                    limit,
                )

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Keep the early TargetRemote pass exhaustive over all tip sources before this match runs.
  2. Replace `unreachable!` with a defensive `continue` (plus a `debug_assert!` or tracing warning) if the invariant is hard to guarantee during refactors.
  3. Add a graph-init fixture with remote-target tips to the snapshot tests so reordering breaks tests, not production.

Example fix

// before
TipRole::TargetRemote => unreachable!("handled above"),

// after
TipRole::TargetRemote => {
    // ordering invariant: consumed in the earlier pass; keep safe during refactors
    debug_assert!(false, "TargetRemote should have been handled above");
    continue;
}
Defensive patterns

Strategy: type-guard

Type guard

fn consumed_earlier(role: &TipRole) -> bool {
    matches!(role, TipRole::TargetRemote)
}

Prevention

When it happens

Trigger: Refactoring the tip-processing loop so TargetRemote tips are no longer fully consumed earlier; adding new tip sources that inject TargetRemote roles after the early phase; merges that reorder the two passes.

Common situations: Graph-initialization refactors in but-graph; extending `TipRole` with new remote-tracking semantics; snapshot-test failures after reordering logic.

Related errors


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