gitbutlerapp/gitbutler · error

A matching parent can't be found in the output graph

Error message

A matching parent can't be found in the output graph

What it means

During rebase, a pick's parents are mapped from the input graph to the output graph via graph_mapping. When a pick has no preserved_parents, each graph parent must map to a node in the output graph; an unmapped index means the new parentage cannot be computed, so the rebase bails rather than guessing parents.

Solutions

  1. Ensure all parents of kept picks are also present in the output graph (don't drop ancestors of kept commits)
  2. Provide preserved_parents explicitly for picks whose parent mapping you control
  3. Recompute graph_mapping after any node removals before running rebase

Example fix

// before: parent dropped from output graph
output_graph.drop(parent_selector)?;
rebase(&graph, &output_graph)?;
// after: keep ancestors or remap children first
let mapped = graph_mapping.get(&parent_idx)
    .context("parent missing; re-add or remap before rebase")?;
rebase(&graph, &output_graph)?;
Defensive patterns

Strategy: validation

Validate before calling

for idx in graph_parents {
    if !graph_mapping.contains_key(&idx) {
        return Err(anyhow!("parent {} missing from output graph", idx));
    }
}

Type guard

fn parents_mapped(graph_parents: &[usize], mapping: &HashMap<usize, usize>) -> bool {
    graph_parents.iter().all(|p| mapping.contains_key(p))
}

Try / catch

let new_idx = graph_mapping.get(&idx)
    .ok_or_else(|| anyhow!("parent dropped from output graph; re-add or set preserved_parents"))?;

Prevention

When it happens

Trigger: Calling rebase (public) when an input-graph parent index of a pick is absent from graph_mapping — typically because that parent node was dropped/filtered from the output graph (e.g. an ancestor pick removed) while descendants still reference it.

Common situations: Dropping or filtering out parent commits from the rebase plan while children still depend on them; custom step transformations that remove nodes without remapping descendant parentage; bugs in callers building the output graph.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/3350537f6efe15c1. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-rebase/src/graph_rebase/rebase.rs:48

        for step_idx in steps_to_pick {
            // Do the frikkin rebase man!
            let step = self.graph[step_idx].clone();
            let new_idx = match step {
                Step::Pick(pick) if !pick.mutable => {
                    // Immutable picks are copied verbatim: the commit keeps its
                    // id, so there's no cherry-pick to run and nothing to record
                    // in the history mapping.
                    output_graph.add_node(Step::Pick(pick))
                }
                Step::Pick(pick) => {
                    let graph_parents = collect_ordered_parents(&self.graph, step_idx);
                    let ontos = match pick.preserved_parents.clone() {
                        Some(ontos) => ontos,
                        None => graph_parents
                            .iter()
                            .map(|idx| {
                                let Some(new_idx) = graph_mapping.get(idx) else {
                                    bail!("A matching parent can't be found in the output graph");
                                };

                                match output_graph[*new_idx] {
                                    Step::Pick(Pick { id, .. }) => Ok(id),
                                    _ => bail!("A parent in the output graph is not a pick"),
                                }
                            })
                            .collect::<Result<Vec<_>>>()?,
                    };

                    let outcome = cherry_pick(
                        &self.repo,
                        pick.id,
                        &ontos,
                        pick.pick_mode,
                        pick.tree_merge_mode,
                        pick.sign_commit,
                    )?;

View on GitHub (pinned to 58e5313667)