gitbutlerapp/gitbutler · error · ExplainedRejection

Cannot {verb}: {rejected}:

Error message

Cannot {verb}: {rejected}:

What it means

The full-form rejection error from but's utils: the operation was rejected due to RejectedChanges, the workspace projection loaded successfully, and write_report() appended a per-path report (paths, coarse reason, and which workspace commits/branches the hunks depend on) after the 'Cannot <verb>: N rejected changes:' header. This is the explanation, not just the failure.

Source

Thrown at crates/but/src/utils/rejection.rs:119

        }
    };
    let (repo, ws) = (&repo, &ws);

    let target_branch = match &target {
        Target::Commit(commit) => branch_of_commit(ws, commit.commit_id, None),
        Target::Branch(name) => Some(name.clone()),
        Target::NewBranch(_) => None,
    };

    let changes = match explain_rejections(repo, ws, &rejected.0, target_branch.as_deref()) {
        Ok(changes) => changes,
        Err(other) => return other,
    };

    let mut message = format!("Cannot {verb}: {rejected}:\n");
    // Writing to a String cannot fail.
    let _ = write_report(&mut message, &changes, &target, target_branch.as_deref());
    anyhow::Error::new(ExplainedRejection(message.trim_end().to_string()))
}

/// A single rejected change, enriched with the workspace dependencies that
/// explain why it could not be applied.
struct RejectedChange {
    /// The worktree-relative path of the rejected change.
    path: BString,
    /// The coarse reason the change was rejected.
    reason: RejectionReason,
    /// The hunks of this change that depend on a commit in the workspace,
    /// together with the commit/branch they depend on.
    ///
    /// Empty when the rejection was not caused by a dependency, or when the
    /// dependency could not be pinpointed to a specific hunk.
    dependencies: Vec<HunkDependency>,
    /// When a dependency rejection cannot be pinpointed to a hunk (adjacent
    /// insertions, for example, do not intersect as ranges), the branches
    /// whose workspace commits also touch this path — the likely dependency.

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Read the report body: each listed path names the commit/branch the change depends on; include that dependency in the operation (apply/move it too) or apply to a target that already contains it.
  2. Reorder operations so dependencies are applied before dependents.
  3. If the dependency is intentionally excluded, expect the rejection and choose a different operation (e.g. apply the whole stack).
  4. Use the listed worktree-relative paths to cross-check which files caused each hunk rejection.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await api.applyChanges(...);
} catch (e) {
  const m = String(e?.message ?? e);
  if (/Cannot .*rejected changes:\n/.test(m)) {
    const report = m.split('\n').slice(1).join('\n');
    showDependencyReport(report); // lists paths + the commits they depend on
  }
  throw e;
}

Prevention

When it happens

Trigger: Applying changes whose hunks depend on commits outside the selected target: e.g. moving/applying a commit whose parent changes are not part of the target branch set, or editing files whose workspace dependencies are not included.

Common situations: Cherry-applying an old commit whose context lines only exist because of a commit not being applied; ordering issues when moving commits across stacks; partial application attempts of a series.

Related errors


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