gitbutlerapp/gitbutler · error
Expected target tip selector to point to a pick
Error message
Expected target tip selector to point to a pick
What it means
During upstream integration, preserve_pick_parents expects the selector given by the caller to point at a Step::Pick in the rebase editor. If lookup_step returns any other step kind, the operation cannot preserve the target tip's original parents and the library bails.
Solutions
- Verify the selector was derived from the current editor plan (after any rewrites) and points at a Pick step
- Check that the target tip commit was actually added as a pick during plan construction; if the target is a merge/checkout fallback, pass a selector to the fallback step instead
- Recompute the target-tip selector after any plan mutation rather than caching it
- Report/inspect the editor step list (editor.lookup_step) to see what the selector actually points at
Example fix
// before: assuming the selector is a pick
preserve_pick_parents(editor, selector)?;
// after: validate the step kind first
match editor.lookup_step(selector)? {
Step::Pick(_) => preserve_pick_parents(editor, selector)?,
other => anyhow::bail!("target tip selector points at {:?}, not a pick", other),
} Defensive patterns
Strategy: validation
Validate before calling
let step = editor.lookup_step(selector)?;
if !matches!(step, Step::Pick(_)) {
anyhow::bail!("selector must point at a Pick step");
} Type guard
fn is_pick(step: &Step) -> bool { matches!(step, Step::Pick(_)) } Try / catch
match integrate_upstream_with_hints(...) {
Err(e) if e.to_string().contains("point to a pick") => recompute_selector_and_retry(),
r => r,
} Prevention
- Recompute target-tip selectors after any rebase-plan mutation
- Never cache selectors across plan rewrites
- Assert the step kind at selection time, not at use time
- Log the editor step list when selector resolution fails
When it happens
Trigger: integrate_upstream_with_hints or replace_checkout_ref_with_fallback passing a target tip selector that resolves to a non-Pick step (e.g. a CheckOut/exec step or an empty/removed step) in the editor's step list.
Common situations: Integrating a target branch whose tip was materialized as a checkout/fallback rather than a pick, or stale selectors computed before the rebase plan was rewritten, so the selector no longer points at the pick that represented the target tip.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- An octopus merge commits must have at least two parents
- Base commit must exist if provided
- Cannot merge : merging into / resulted in conflicts. Rebase…
- Cherry picks can only be done for single-parent commits…
- Encountered conflict when merging tree
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/256c96e121086a2f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-workspace/src/upstream_integration.rs:1162
child: head_ref_selector,
parent: head_ref_selector,
},
SelectorSet::All,
SelectorSet::All,
false,
)?;
preserve_pick_parents(editor, target_tip_selector)?;
editor.add_edge(head_ref_selector, target_tip_selector, 0)?;
Ok((head_ref_selector, fallback_ref_name))
}
fn preserve_pick_parents<M: RefMetadata>(
editor: &mut Editor<'_, '_, M>,
selector: Selector,
) -> Result<()> {
let Step::Pick(mut pick) = editor.lookup_step(selector)? else {
bail!("Expected target tip selector to point to a pick");
};
let commit = editor.find_commit(pick.id)?;
// TODO: Teach but-rebase to treat immutable reference parents as object
// anchors. Until then, preserve the target tip's original parents here so
// graph-rebase materializes the fallback branch at the exact target ref
// object instead of replaying merge-based target history into an equivalent
// local rewrite.
pick.preserved_parents = Some(commit.inner.parents.iter().copied().collect());
editor.replace(selector, Step::Pick(pick))?;
Ok(())
}
/// Fast-forward the local branch that tracks a remote `target_ref`, preferring the same name.
///
/// Local target refs, missing tracking branches, checked-out branches, and non-fast-forward updates
/// are left unchanged.
pub fn fast_forward_local_tracking_branch(
repo: &gix::Repository,View on GitHub (pinned to 58e5313667)