gitbutlerapp/gitbutler · error

Cannot land ` `: it would publish conflicted commit ( )…

Error message

Cannot land `{branch}`: it would publish {} conflicted commit{} ({}). Resolve them first with `but resolve <commit>`.

What it means

`validate_branch_landing` refuses to land a branch whose publish set contains conflicted commits. Pushing or merging conflicted commits would produce a broken or rejected integration on the target, so GitButler blocks the land and lists the conflicted commit IDs, directing the developer to resolve them with `but resolve <commit>` first.

Solutions

  1. Run `but resolve <commit>` for each listed commit and resolve the conflicts, then retry the land.
  2. Use the GitButler UI/TUI conflict-resolution flow to clear all conflicted commits.
  3. Remove or rework the conflicted commits if they are no longer needed.

Example fix

// before
but land feature-x   // refused: would publish 2 conflicted commits (abc123, def456)
// after
but resolve abc123
but resolve def456
but land feature-x
Defensive patterns

Strategy: validation

Validate before calling

// check for conflicted commits in the publish set before landing
const ws = await api.workspaceState();
const conflicted = ws.stacks.flatMap(s => s.commits).filter(c => c.conflicted);
if (conflicted.length) throw new Error(`Resolve ${conflicted.map(c => c.id).join(', ')} before landing`);

Type guard

function hasConflictedCommits(commits) {
  return commits.some(c => c.conflicted === true);
}

Try / catch

try {
  await api.land(branch);
} catch (e) {
  const ids = String(e).match(/\(([a-f0-9,\s]+)\)/)?.[1]?.split(',').map(s => s.trim());
  if (ids) {
    for (const id of ids) await api.resolve(id);
    await api.land(branch);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling `branch_land` when `scan.conflicted` is non-empty — one or more commits in the segment(s) that would be published are in a conflicted state (e.g. after a rebase or pull left unresolved conflicts).

Common situations: After `but pull` / `but rebase` introduced conflicts that were not fully resolved; applying upstream changes that conflict with local commits; returning to a workspace after an interrupted conflict-resolution session.

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/1f992569eb6f50f1. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-api/src/land/mod.rs:445

            bail!(
                "Refusing to land `{branch}`: {} commit(s) on unnamed segment(s) below it would \
                 also be published to {target_display}. Pass --whole-stack to land `{branch}` \
                 together with everything below it.",
                scan.commits_below,
            );
        }
        bail!(
            "Refusing to land `{branch}`: it is stacked on top of {} other segment(s) ({}) \
             whose commits would also be published to {target_display}. Land the bottom segment \
             `{}`, or pass --whole-stack to land `{branch}` together with everything below it.",
            scan.lower_segments.len(),
            scan.lower_segments.join(", "),
            scan.lower_segments.last().expect("non-empty checked above"),
        );
    }

    if !scan.conflicted.is_empty() {
        bail!(
            "Cannot land `{branch}`: it would publish {} conflicted commit{} ({}). \
             Resolve them first with `but resolve <commit>`.",
            scan.conflicted.len(),
            if scan.conflicted.len() == 1 { "" } else { "s" },
            scan.conflicted.join(", "),
        );
    }
    Ok(scan.lower_segments)
}

/// Fetch the target's fetch remote and record the outcome on the project, mirroring the
/// bookkeeping `fetch_from_remotes` performs so `last_fetched` and auto-fetch scheduling stay
/// accurate for targeted fetches too.
fn fetch_target_remote(ctx: &Context, remote: &str) -> anyhow::Result<()> {
    use anyhow::Context as _;

    let result = ctx.fetch(remote, Some("land".to_string()));
    let timestamp = std::time::SystemTime::now();

View on GitHub (pinned to 58e5313667)