gitbutlerapp/gitbutler · error

Invalid parents to disconnect: SelectorSet::None is not allo

Error message

Invalid parents to disconnect: SelectorSet::None is not allowed

What it means

`Editor::disconnect_segment_from` documents (and enforces) that `parents_to_disconnect = SelectorSet::None` is only legal when `skip_reconnect_step` is true. With None-parents and reconnection enabled, every disconnected child would need to be reconnected to an empty parent set — an ill-defined operation — so the editor rejects the combination up front.

Source

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

            SelectorSet::Some(children) => Some(
                children
                    .as_slice()
                    .iter()
                    .map(|from_child| from_child.to_selector(self))
                    .collect::<Result<Vec<_>>>()?
                    .into_iter()
                    .map(|selector| self.history.normalize_selector(selector))
                    .collect::<Result<Vec<_>>>()?,
            ),
        };

        let parents_to_disconnect = match parents_to_disconnect {
            SelectorSet::All => None,
            SelectorSet::None => {
                if skip_reconnect_step {
                    Some(Vec::new())
                } else {
                    return Err(anyhow!(
                        "Invalid parents to disconnect: SelectorSet::None is not allowed"
                    ));
                }
            }
            SelectorSet::Some(parents) => Some(
                parents
                    .as_slice()
                    .iter()
                    .map(|from_parent| from_parent.to_selector(self))
                    .collect::<Result<Vec<_>>>()?
                    .into_iter()
                    .map(|selector| self.history.normalize_selector(selector))
                    .collect::<Result<Vec<_>>>()?,
            ),
        };

        // Edges to children.
        let incoming_edges = self

View on GitHub (pinned to caf1f223d3)

Solutions

  1. If you really want to disconnect with no parents and no reconnection, pass `skip_reconnect_step = true`
  2. If children should be reattached, specify the parents explicitly with `SelectorSet::Some(SomeSelectors::new([...])?)`
  3. To disconnect from everything, use `SelectorSet::All`
  4. Re-read the doc comment on `disconnect_segment_from` — the error conditions are enumerated there verbatim

Example fix

// before
editor.disconnect_segment_from(target, children, SelectorSet::None, /* skip_reconnect_step */ false)?; // Err

// after — pick the intended contract:
editor.disconnect_segment_from(target, children, SelectorSet::None, true)?; // no reconnect
// or
editor.disconnect_segment_from(target, children, SelectorSet::Some(SomeSelectors::new(parents)?), false)?; // reconnect to explicit parents
Defensive patterns

Strategy: validation

Validate before calling

// Mirror the documented contract before calling:
if matches!(parents_to_disconnect, SelectorSet::None) {
    anyhow::ensure!(skip_reconnect_step,
        "SelectorSet::None parents requires skip_reconnect_step = true");
}
editor.disconnect_segment_from(target, children, parents_to_disconnect, skip_reconnect_step)?;

Prevention

When it happens

Trigger: Calling `disconnect_segment_from(target, children, SelectorSet::None, false)` — i.e. disconnecting children while specifying no parents to reconnect them to, without opting out of the reconnect step.

Common situations: Callers translating a UI action like 'detach these children only' into editor calls without realizing the default contract; porting code from an older signature where None had different semantics.

Related errors


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