gitbutlerapp/gitbutler · error

Refusing to land ` ` with --whole-stack: it is not the top…

Error message

Refusing to land `{branch}` with --whole-stack: it is not the top of its stack. --whole-stack lands the entire stack; name its top segment `{top}` instead.

What it means

`validate_branch_landing` refuses a `--whole-stack` land when the target branch is not the topmost segment of its stack but a named segment sits above it. `--whole-stack` is defined to land the entire stack, and landing a mid-stack branch that way would strand the commits above it, so GitButler rejects the request and names the actual top segment to use instead.

Solutions

  1. Land the named top segment instead: `but land <top-segment> --whole-stack` (the error message names it).
  2. Drop or rebase off the segments above first, then re-run `but land <branch> --whole-stack`.
  3. Land without `--whole-stack` if you only intend to publish this branch's commits.

Example fix

// before
but land middle-branch --whole-stack   // refused: not top of stack
// after
but land top-branch --whole-stack      # lands the entire stack including middle-branch
Defensive patterns

Strategy: validation

Validate before calling

// before landing with --whole-stack, verify the branch is the stack top
const stack = await api.listStacks();
const s = stack.find(s => s.branches.includes(branch));
const isTop = s.branches[s.branches.length - 1] === branch;
if (!isTop) throw new Error(`Land the top segment ${s.branches.at(-1)} with --whole-stack instead`);

Type guard

function isStackTop(branch: string, stackBranches: string[]): boolean {
  return stackBranches[stackBranches.length - 1] === branch;
}

Try / catch

try {
  await api.land(branch, { wholeStack: true });
} catch (e) {
  const m = String(e).match(/name its top segment `([^`]+)`/);
  if (m) await api.land(m[1], { wholeStack: true }); // retry with the named top segment
  else throw e;
}

Prevention

When it happens

Trigger: Calling `branch_land` with `whole_stack: true` (CLI `but land <branch> --whole-stack`) where `scan.has_upper` is true and `scan.upper_segments` has a named first segment — i.e. the branch has a segment above it in the same stack.

Common situations: Working in a stacked-branch workflow, forgetting that the stack has more segments on top; a branch ref above was renamed so the developer assumes their branch is the top; scripting lands on a fixed branch name that is no longer the stack 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/acc7ea1fdc37f5c2. Report an issue: GitHub.

Appendix: source

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

/// commits in any segment that would be published (the same guard `but push` applies before
/// sending commits to a remote). All computed from the graph workspace, not stack projections.
/// Returns the named lower segments that land together with `branch` — non-empty only for a
/// validated `--whole-stack` land.
fn validate_branch_landing(
    ctx: &mut Context,
    branch: &str,
    target_display: &str,
    whole_stack: bool,
) -> anyhow::Result<Vec<String>> {
    let Some(scan) = scan_stack(ctx, branch)? else {
        return Ok(Vec::new());
    };

    if whole_stack && scan.has_upper {
        // Judge "top of the stack" by position, not by names: a segment whose branch ref was
        // deleted still holds commits that "land the entire stack" would have to include.
        if let Some(top) = scan.upper_segments.first() {
            bail!(
                "Refusing to land `{branch}` with --whole-stack: it is not the top of its stack. \
                 --whole-stack lands the entire stack; name its top segment `{top}` instead.",
            );
        }
        bail!(
            "Refusing to land `{branch}` with --whole-stack: it is not the top of its stack — \
             unnamed segment(s) with commits sit above it (their branch refs no longer exist), so \
             landing `{branch}` would not land the entire stack.",
        );
    }
    // Key off the commits below, not the segment names: segments whose branch ref was deleted are
    // unnamed but their commits would be published all the same.
    let publishes_below = scan.commits_below > 0 || !scan.lower_segments.is_empty();
    if publishes_below && !whole_stack {
        if scan.lower_segments.is_empty() {
            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}` \

View on GitHub (pinned to 58e5313667)