gitbutlerapp/gitbutler · error

No changes were selected to move

Error message

No changes were selected to move

What it means

move_uncommitted_changes transfers selected uncommitted changes from a linked worktree into the destination, but an explicitly provided selection (Some) that is empty means nothing was chosen. The library treats an empty selection as a caller mistake rather than a silent no-op.

Solutions

  1. Pass None instead of an empty selection when nothing should move (or skip the call entirely)
  2. Validate the selection is non-empty in the caller/UI before invoking
  3. Rebuild the selection from the current diff if the source list was computed against stale state

Example fix

// before: forwarding an empty selection
move_uncommitted_changes(ctx, &wt, selection /* = [] */, None)?;
// after: skip when empty
if selection.as_ref().is_some_and(|s| s.is_empty()) {
    return Ok(()); // or surface "nothing selected" in the UI
}
move_uncommitted_changes(ctx, &wt, selection, None)?;
Defensive patterns

Strategy: validation

Validate before calling

if let Some(sel) = &selection {
    anyhow::ensure!(!sel.is_empty(), "select at least one change to move, or pass None");
}

Try / catch

match move_uncommitted_changes(ctx, &wt, selection, None) {
    Err(e) if e.to_string().contains("No changes were selected") => {
        // surface 'nothing selected' in UI or skip as no-op
    }
    r => r,
}

Prevention

When it happens

Trigger: Calling move_uncommitted_changes with Some(selection) where selection.is_empty() — e.g. the UI collected zero hunks/files, the user confirmed a move dialog with nothing ticked, or a filter produced an empty hunk list.

Common situations: Agent-driven automation building a selection from a diff that turned out empty; a UI bug sending an empty array instead of None; a user pressing 'move' without selecting any files or hunks.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at crates/but-workspace/src/worktrees.rs:189

/// `DiffSpec`s. If `None`, every uncommitted change in the worktree is moved and `context_lines`
/// is unused.
pub fn move_uncommitted_changes(
    main_repo: &gix::Repository,
    worktree_repo: &gix::Repository,
    selection: Option<Vec<DiffSpec>>,
    context_lines: u32,
) -> anyhow::Result<MoveUncommittedChangesOutcome> {
    let worktree_head_tree = worktree_repo.head_tree_id_or_empty()?.detach();

    let moved_tree = match selection {
        None =>
        {
            #[expect(deprecated)]
            worktree_repo.create_wd_tree(0)?
        }
        Some(selection) => {
            if selection.is_empty() {
                bail!("No changes were selected to move");
            }
            let outcome = but_core::tree::create_tree(
                worktree_repo,
                worktree_head_tree,
                selection,
                context_lines,
            )?;
            let rejected: Vec<_> = outcome
                .rejected_specs
                .iter()
                .map(|(reason, spec)| format!("{reason:?}: {}", spec.path))
                .collect();
            if !rejected.is_empty() {
                bail!(
                    "Some selected changes no longer match the worktree's uncommitted changes: {}",
                    rejected.join(", ")
                );
            }

View on GitHub (pinned to 58e5313667)