gitbutlerapp/gitbutler · error
Cannot reorder '{subject_branch_name}' in single-branch mode
Error message
Cannot reorder '{subject_branch_name}' in single-branch mode without branch order metadata What it means
In single-branch mode, reordering empty branches is persisted through branch-stack-order metadata. The guard fires when the RefMetadata implementation reports can_persist_branch_stack_order() == false, which for the legacy backend means the metadata database handle is absent or opened read-only. Without a writable order store the reorder would be silently lost, so the operation is refused.
Source
Thrown at crates/but-workspace/src/branch/move_branch.rs:255
let (source_stack, subject_segment) = &source;
let (destination_stack, _) = &destination;
let entrypoint = workspace.ref_name().map(ToOwned::to_owned);
// A branch that owns commits can only be reordered within its current stack in
// single-branch mode. Moving it across stacks would change commit ownership and needs a
// real rebase.
if !subject_segment.commits.is_empty() && !same_stack(source_stack, destination_stack) {
bail!("Moving a non-empty branch in single-branch mode is not yet supported");
}
// Reordering same-target empty refs only changes which empty segment is displayed first.
// If their targets differ, however, the subject crosses commit-owning segments and its ref
// must move with it or those commits would be projected as belonging to the empty branch.
let move_requires_graph_update = !subject_segment.commits.is_empty()
|| successful_rebase.reference_target(subject_branch_name)?
!= successful_rebase.reference_target(target_branch_name)?;
let existing_order = {
let (_repo, meta) = successful_rebase.repo_and_meta_mut();
if !meta.can_persist_branch_stack_order() {
bail!(
"Cannot reorder '{subject_branch_name}' in single-branch mode without branch order metadata"
);
}
// Reorder against the existing chain. A movable subject is always part of `branch_order`
// (that's what makes it a projected segment), so the first lookup normally succeeds. The
// target and entrypoint lookups are defensive fallbacks so that, should the projection ever
// surface a segment that isn't tracked yet, we extend the real chain instead of clobbering
// it down to just the moved refs.
match meta.branch_stack_order(subject_branch_name)? {
Some(order) => order,
None => match meta.branch_stack_order(target_branch_name)? {
Some(order) => order,
None => entrypoint
.as_ref()
.map(|entrypoint| meta.branch_stack_order(entrypoint.as_ref()))
.transpose()?
.flatten()
.unwrap_or_else(|| stack_branch_order(source_stack)),View on GitHub (pinned to caf1f223d3)
Solutions
- Run the operation with a writable metadata backend (open the project database normally, not read-only).
- If you implement RefMetadata yourself, implement the branch_stack_order family and return true from can_persist_branch_stack_order().
- Skip reorder UI actions when the metadata handle is read-only.
Example fix
// before let outcome = move_branch(editor, &subject, &target)?; // after let (_, meta) = successful_rebase.repo_and_meta_mut(); ensure!(meta.can_persist_branch_stack_order(), "metadata read-only; reorder disabled"); let outcome = move_branch(editor, &subject, &target)?;
Defensive patterns
Strategy: validation
Validate before calling
let (_repo, meta) = successful_rebase.repo_and_meta_mut();
if !meta.can_persist_branch_stack_order() {
// read-only or missing metadata backend: skip reorder actions
return Ok(disabled_outcome());
} Try / catch
match move_branch(editor, &subject, &target) {
Err(err) if err.to_string().contains("without branch order metadata") => {
Err(anyhow!("reorder requires a writable metadata store; reopen the project database"))
}
other => other,
} Prevention
- Open the project metadata read-write for any mutating operation.
- If you implement RefMetadata, implement the branch_stack_order API surface before enabling reorders.
- Gate reorder UI on can_persist_branch_stack_order().
When it happens
Trigger: Calling the ad-hoc reorder path while meta is a read-only or database-less RefMetadata (but-meta legacy: db.is_none() || read_only), e.g. inspection tools, dry-run traversals, or tests with in-memory metadata.
Common situations: Opening a project in a read-only/preview context and then attempting a reorder; custom RefMetadata implementations that never implemented order persistence; unit tests using stub metadata hitting production code paths.
Related errors
- Read-only metadata can't prune branch stack order references
- Moving a non-empty branch in single-branch mode is not yet s
- Cannot unapply branch '{branch}' from an ad-hoc workspace be
- targetCommitId in project_meta.toml is null
- Failed to communicate with LM Studio server: ${error instanc
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/8fc52555e68ef7e0.
Report an issue: GitHub.