gitbutlerapp/gitbutler · error

BUG: It should not be possible to omit sources

Error message

BUG: It should not be possible to omit sources

What it means

`resolve_sources` in but's legacy `move` command matches on which source kinds were detected (committed changes / untracked changes / branches). The `(None, None, None)` arm panics with 'BUG: It should not be possible to omit sources' because the argument parsing layer is expected to reject an empty `<SOURCES>` earlier, so resolution should never see zero sources. Hitting it means an empty or fully-unrecognized SOURCES argument slipped past that earlier validation.

Source

Thrown at crates/but/src/command/legacy/move.rs:868

            }

            // It doesn't appear as if we need to sort DiffSpecs when they're resolved on a file
            // level. For the future hunk level DiffSpecs we may need to, however.
            let changes = NonEmpty::from_vec(builder.into_diff_specs())
                .expect("BUG: Cannot possibly not have any changes here");

            Ok(ResolvedSources::CommittedChanges((source_commit, changes)))
        }
        (None, None, Some(branches)) => {
            if !branches.tail.is_empty() {
                Err(bad_input("Branches can only be moved one at a time")
                    .arg_name("<SOURCES>")
                    .into())
            } else {
                Ok(ResolvedSources::Branch(branches.head))
            }
        }
        (None, None, None) => panic!("BUG: It should not be possible to omit sources"),
        (_, _, _) => Err(bad_input("Mixing source types is not allowed")
            .hint("You can only move one kind of source (e.g. commits) at a time")
            .arg_name("<SOURCES>")
            .into()),
    }
}

pub fn run(
    ctx: &mut Context,
    meta: &mut impl RefMetadata,
    perm: &mut RepoExclusive,
    move_op: MoveOperation,
) -> anyhow::Result<(MoveOutcome, WorkspaceState)> {
    let snapshot_details = match &move_op {
        MoveOperation::CommitsRelativeTo(_) | MoveOperation::CommitsToNewBranch(_) => {
            SnapshotDetails::new(OperationKind::MoveCommit)
        }
        MoveOperation::ChangesRelativeTo(_) | MoveOperation::ChangesToNewBranch(_) => {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Pass at least one valid source (commit-ish, branch name, or untracked path) in `<SOURCES>`.
  2. If you hit it from code, add a `bad_input` check that errors when resolved sources are empty instead of dispatching.
  3. Check for trailing whitespace/empty args in the wrapper script producing the command line.

Example fix

// before
let sources = resolve_sources(args)?; // panics on (None, None, None)

// after
let sources = resolve_sources(args)?;
if sources.is_empty() {
    return Err(bad_input("At least one <SOURCES> entry is required")
        .arg_name("<SOURCES>")
        .into());
}
Defensive patterns

Strategy: validation

Validate before calling

// wrapper guard before invoking the legacy move path
fn ensure_sources_present(resolved: &ResolvedSources) -> anyhow::Result<()> {
    if resolved.is_empty() {
        return Err(bad_input("At least one <SOURCES> entry is required")
            .arg_name("<SOURCES>")
            .into());
    }
    Ok(())
}

Try / catch

let sources = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| resolve_sources(&args)))
    .unwrap_or_else(|_| panic!("<SOURCES> resolved to nothing — pass a commit, branch, or untracked path"));

Prevention

When it happens

Trigger: Running the legacy move command with a SOURCES argument that parses to zero recognized items (empty string segments, or revision specs that resolve to nothing); programmatic invocation of the resolve path with empty input.

Common situations: Scripts passing an unset/empty shell variable as SOURCES; a CLI parser regression that stops rejecting empty SOURCES; new frontends calling the legacy move internals directly.

Related errors


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