gitbutlerapp/gitbutler · warning · ExplainedRejection

Cannot : :\n

Error message

Cannot {verb}: {rejected}:\n{report}

What it means

The primary explained-rejection error: after an operation (assign/apply) is rolled back, `explain_after_rollback` builds a human-readable report of each rejected change with the workspace dependencies that explain why it could not be applied, wrapped as `ExplainedRejection` with message `Cannot {verb}: {rejected}:\n{report}`.

Solutions

  1. Read the per-path report under the message — it lists each rejected change and the dependency that caused it.
  2. Apply or reassign the blocking dependency changes first, then retry the original operation.
  3. Resolve overlapping/conflicting assignments between branches before re-running.
  4. If the report points to unexpected paths, refresh workspace status and retry — the rejection may be based on stale state.

Example fix

// before: apply everything at once and fail
but.apply_all_changes()?;
// after: inspect rejections and apply in dependency order
match but.apply_all_changes() {
    Err(e) if e.is::<ExplainedRejection>() => {
        for path in parse_rejected_paths(&e.to_string()) {
            assign_to_correct_branch(path)?;
        }
        but.apply_all_changes()?;
    }
    other => other?,
}
Defensive patterns

Strategy: try-catch

Type guard

fn is_explained_rejection(err: &anyhow::Error) -> bool {
    err.downcast_ref::<ExplainedRejection>().is_some()
}

Try / catch

if let Err(e) = assign_changes(&changes) {
    if let Some(rej) = e.downcast_ref::<ExplainedRejection>() {
        eprintln!("{rej}"); // per-path dependency report
        return Err(e);
    }
    return Err(e);
}

Prevention

When it happens

Trigger: Calling an apply/assign operation where some hunks/changes violate workspace rules (e.g. the change belongs to another branch's ownership, conflicts with an applied change, or a dependency in the workspace blocks it) and the operation is rolled back with `rejected > 0`.

Common situations: Trying to assign a file hunk to a branch while an overlapping hunk is already assigned elsewhere; applying a change whose path depends on unapplied workspace changes; exclusive ownership conflicts in multi-branch workspaces.

Related errors


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

Appendix: 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 58e5313667)