gitbutlerapp/gitbutler · error

Commit {} was marked as not conflictable, but resulted in a

Error message

Commit {} was marked as not conflictable, but resulted in a conflicted state

What it means

Each pick in a graph rebase plan carries a `conflictable` flag declaring whether a conflicted outcome is acceptable for that commit. When the cherry pick of a commit marked not-conflictable nevertheless produces a conflicted commit, the rebase bails: the plan's assumptions no longer match the history being rebased (typically a plan built over a stale graph).

Source

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

                                    _ => 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,
                    )?;

                    if matches!(outcome, CherryPickOutcome::ConflictedCommit(_))
                        && !pick.conflictable
                    {
                        bail!(
                            "Commit {} was marked as not conflictable, but resulted in a conflicted state",
                            pick.id
                        );
                    }

                    match outcome {
                        CherryPickOutcome::Commit(new_id)
                        | CherryPickOutcome::ConflictedCommit(new_id)
                        | CherryPickOutcome::Identity(new_id) => {
                            let mut new_pick = pick.clone();
                            new_pick.id = new_id;
                            let new_idx = output_graph.add_node(Step::Pick(new_pick));
                            graph_mapping.insert(step_idx, new_idx);
                            if !pick.exclude_from_tracking {
                                history.update_mapping(pick.id, new_id);
                            }

                            new_idx

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Rebuild the graph and plan from the current state, then execute the fresh plan
  2. Mark such picks conflictable if conflicts are genuinely tolerable for this flow
  3. Serialize planning and execution so no ref updates land in between

Example fix

// before: execute a plan captured earlier
editor.rebase(&stale_plan)?; // may hit the not-conflictable invariant

// after: rebuild the plan right before execution
let graph = but_graph::Graph::from_repo(&repo)?;
let plan = build_plan(&graph);
editor.rebase(&plan)?;
Defensive patterns

Strategy: retry

Validate before calling

// Rust: build the plan immediately before executing it, from a fresh graph
let graph = but_graph::Graph::from_repo(&repo)?;
let plan = build_plan(&graph); // conflictable flags reflect current state
editor.rebase(&plan)?;

Try / catch

On this invariant bail, discard the plan, rebuild graph + plan from current state, and retry once; if it reproduces, report a bug including the commit id from the message.

Prevention

When it happens

Trigger: Executing a plan where `conflictable=false` for a commit whose pick returns `CherryPickOutcome::ConflictedCommit` — history or refs changed between plan creation and execution.

Common situations: Plans computed from cached graphs; refs updated by another window or a background fetch between planning and execution; concurrent worktrees sharing objects.

Related errors


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