gitbutlerapp/gitbutler · error
Expected selector for
Error message
Expected selector for {step:?} to refer to a commit What it means
ToCommitSelector::to_commit_selector normalizes a Selector against the editor's history and asserts that the graph node it points to is a Step::Pick (a commit). If the normalized selector resolves to any other step kind (e.g. a Step::Reference), the requested conversion is impossible and the library bails.
Solutions
- Confirm the selector points at a Pick step before calling to_commit_selector (inspect editor.graph[selector.id])
- Use to_reference_selector instead if the selector names a reference
- Re-obtain the selector from the current editor state after graph mutations
Example fix
// before: selector names a branch -> error
let commit = selector.to_commit_selector(&editor)?;
// after
let step = &editor.graph[selector.id];
let commit = match step {
Step::Pick(_) => selector.to_commit_selector(&editor)?,
Step::Reference { .. } => bail!("use a commit selector here"),
_ => bail!("unsupported step"),
}; Defensive patterns
Strategy: type-guard
Validate before calling
let step = &editor.graph[selector.id];
if !matches!(step, Step::Pick(_)) { return Err(anyhow!("not a commit selector")); } Type guard
fn is_pick(selector: Selector, editor: &Editor<impl RefMetadata>) -> bool {
matches!(editor.graph[selector.id], Step::Pick(_))
} Try / catch
match selector.to_commit_selector(&editor) {
Ok(commit) => commit,
Err(_) => bail!("selector does not refer to a commit; check step kind first"),
} Prevention
- Track which selectors name commits vs references
- Re-normalize selectors after every graph mutation
When it happens
Trigger: Passing a Selector that resolves to a reference/other non-pick step into any API taking impl ToCommitSelector — e.g. calling to_commit_selector on a selector obtained for a branch/reference step, or an id that after normalize_selector lands on a non-Pick node.
Common situations: Mixing up selectors for references and commits when scripting graph edits (move_branch vs commit operations); using a stale selector index after the graph was mutated so normalization resolves elsewhere.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Expected selector for
- Expected selector to point to a pick
- Expected selector to point to a reference
- Expected target tip selector to point to a pick
- Reference target in rebase result is not a commit
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/9f15d931128dd5f9.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-rebase/src/graph_rebase/mod.rs:221
///
/// Hash, PartialEq, and Eq are implemented for this struct. Because selectors
/// are a pointer to a node in a particular version of the Editor's internal
/// representation, it means that you can have two selectors that when
/// normalised point to the same node. If you want to ensure you have just one
/// selector to a given node, make sure you are working with selectors all
/// normalised to the latest revision of the Editor.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Selector {
id: StepGraphIndex,
revision: usize,
}
impl ToCommitSelector for Selector {
fn to_commit_selector(&self, editor: &Editor<impl RefMetadata>) -> Result<Selector> {
let selector = editor.history.normalize_selector(*self)?;
let step = &editor.graph[selector.id];
if !matches!(step, Step::Pick(_)) {
bail!("Expected selector for {step:?} to refer to a commit");
}
Ok(selector)
}
}
impl ToReferenceSelector for Selector {
fn to_reference_selector(&self, editor: &Editor<impl RefMetadata>) -> Result<Selector> {
let selector = editor.history.normalize_selector(*self)?;
let step = &editor.graph[selector.id];
if !matches!(step, Step::Reference { .. }) {
bail!("Expected selector for {step:?} to refer to a reference");
}
Ok(selector)
}
}
View on GitHub (pinned to 58e5313667)