gitbutlerapp/gitbutler · error

BUG: No CLI argument for resolved commit id

Error message

BUG: No CLI argument for resolved commit id

What it means

Internal alignment invariant when rejecting 'source is also the target' in the move command: resolved_commits are built one-to-one from the CLI source arguments, so index i into resolved_commits must have a matching args[i]. The expect fires only if that pairing was broken by a code change (resolutions coming from somewhere other than args) - user input cannot desynchronize them.

Source

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

            Ok(MoveOperation::StackBranch(StackBranchOnOperation {
                source_branch,
                target_branch,
            }))
        }
        ResolvedSources::Commits {
            resolved_commits,
            args,
        } => {
            if let MoveTarget::Commit {
                commit: target_commit,
                ..
            } = &target
            {
                for (i, source_commit) in resolved_commits.iter().enumerate() {
                    if source_commit.commit_id == target_commit.commit_id {
                        let unresolved_source = args
                            .get(i)
                            .expect("BUG: No CLI argument for resolved commit id");
                        return Err(bad_input("Source cannot also be target")
                            .arg_value(unresolved_source.to_string())
                            .arg_name(format!("--{side}"))
                            .hint(format!("Trying to move items {side} '{unresolved_source}'? Remove '{unresolved_source}' from '<SOURCES>' and try again!"))
                            .into());
                    }
                }
            }

            Ok(MoveOperation::CommitsRelativeTo(
                MoveCommitsRelativeToOperation {
                    sources: resolved_commits,
                    target,
                },
            ))
        }
        ResolvedSources::CommittedChanges((source_commit, changes)) => Ok(
            MoveOperation::ChangesRelativeTo(MoveChangesRelativeToOperation {

View on GitHub (pinned to 2497b8007a)

Solutions

  1. Report the exact 'but move ...' invocation - unreachable via well-formed input
  2. Maintainer: zip args with resolved_commits when building them so the pairing is structural rather than index-based
  3. Add a test moving a commit relative to itself to lock in the error path

Example fix

// before
for (i, source_commit) in resolved_commits.iter().enumerate() {
    if source_commit.commit_id == target_commit.commit_id {
        let unresolved_source = args.get(i).expect("BUG: No CLI argument for resolved commit id");
        // ...

// after - pair each resolution with its argument once, structurally
for (source_commit, unresolved_source) in resolved_commits.iter().zip(args) {
    if source_commit.commit_id == target_commit.commit_id {
        // error using unresolved_source directly
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// Construction-side invariant: resolutions and args are built together
let resolved: Vec<_> = args.iter().map(|arg| resolve_commit(arg).map(|c| (c, arg.clone()))).collect::<Result<_>>()?;
// pairs are now structural; indexing cannot desync

Prevention

When it happens

Trigger: A refactor resolves commits from a different source than args (workspace stack entries, implicit commits) making the indexes diverge; a multi-source resolution path that skips or reorders args.

Common situations: Changes to move's source resolution (branch, commit, file selection) without updating the paired-args assumption; not reachable through valid CLI usage.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17). Data as JSON: /api/errors/1cd29eb467c7ed54. Report an issue: GitHub.