gitbutlerapp/gitbutler · error

You are in conflict resolution mode. Finish with `but…

Error message

You are in conflict resolution mode. Finish with `but resolve finish` or cancel with `but resolve cancel` first.

What it means

Thrown by apply_resolutions when the operating mode is Edit (i.e. an edit/conflict-resolution session is already active). Applying resolutions requires a clean state; the user must finish or cancel the current session first. The message spells out both escape commands.

Solutions

  1. Run `but resolve finish` to complete the active session.
  2. Or run `but resolve cancel --force`/`cancel` to abort it, then retry.
  3. Check state with `but status`.

Example fix

// before
but resolve abc123 file.rs --ours
// after
but resolve finish && but resolve abc123 file.rs --ours
Defensive patterns

Strategy: validation

Validate before calling

const mode = getOperatingMode();
if (mode === 'edit') { await exec('but resolve finish'); } // or cancel
await butResolve(commit, path, args);

Type guard

const isEditMode = (m: OperatingMode) => m === 'edit';

Try / catch

try { await butResolve(...) } catch (e) { if (String(e).includes('conflict resolution mode')) { await exec('but resolve finish'); retry(); } }

Prevention

When it happens

Trigger: Running `but resolve` (apply_resolutions) while the context's operating_mode is OperatingMode::Edit — an unfinished `but edit`/resolution session.

Common situations: A previous `but resolve` or edit session was left open (crashed terminal, forgotten session); users then try to apply new resolutions on top.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at crates/but/src/command/legacy/resolve.rs:882

    out: &mut OutputChannel,
    commit: Option<&str>,
    target: &str,
    flags: ResolutionFlags,
    mut read: impl std::io::Read,
) -> Result<()> {
    use but_api::resolve::{HunkResolution, ResolutionSpec};

    let ResolutionFlags {
        ours,
        theirs,
        ai,
        file,
    } = flags;

    let t = theme::get();
    let mode = operating_mode(ctx)?.operating_mode;
    if matches!(mode, OperatingMode::Edit(_)) {
        bail!(
            "You are in conflict resolution mode. Finish with `but resolve finish` or cancel with `but resolve cancel` first."
        );
    }
    let Some(conflict_target) = target_conflicted_commit(ctx, commit)? else {
        bail!("No conflicted commits found.");
    };
    let commit_oid = conflict_target.commit_oid;

    // Split a trailing `:<number>` off the target; everything else is the path.
    let (path, hunk) = match target.rsplit_once(':') {
        Some((path, number))
            if !number.is_empty() && number.bytes().all(|b| b.is_ascii_digit()) =>
        {
            (path, Some(number.parse::<usize>()?))
        }
        _ => (target, None),
    };

View on GitHub (pinned to 58e5313667)