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
- Read the per-path report under the message — it lists each rejected change and the dependency that caused it.
- Apply or reassign the blocking dependency changes first, then retry the original operation.
- Resolve overlapping/conflicting assignments between branches before re-running.
- 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
- Read the per-path report — it names the blocking dependency for every rejected change.
- Keep branch assignments non-overlapping for the same paths.
- Apply dependency changes before dependent ones.
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
- No stacks found in the workspace
- return anyhow::Error::new(ExplainedRejection(format!
- a committed transaction always materializes a workspace
- Ad-hoc (single-branch) branch moves are not supported…
- Alternative hunks algorithms still didn't produce properly…
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)