gitbutlerapp/gitbutler · warning
target OID must exist when ahead calculation is enabled
Error message
target OID must exist when ahead calculation is enabled
What it means
Defensive expect in 'but branch list': when commits-ahead counting is enabled (the default; disabled with --no-ahead), the workspace target's commit id must be present. Upstream, target_oid is resolved exactly when '!empty || ahead || check_merge' (list.rs:47-55), and ResolvedTarget::oid() returns a plain ObjectId - resolution either succeeds or propagates its error via '?'. With the current wiring the expect cannot fire; it guards against future flag-wiring desync. When the target genuinely cannot be resolved (workspace without a base branch), users see the propagated resolution error instead of this panic.
Source
Thrown at crates/but/src/command/legacy/branch/list.rs:194
// in the target and has no commits ahead to show.
repo.merge_base_with_graph(branch.head, target_oid, &mut graph)
.map(|merge_base| merge_base.detach() != branch.head)
.unwrap_or(true)
})
.take(num_branches_to_take)
.collect()
} else {
branches.into_iter().take(num_branches_to_take).collect()
};
let has_more_branches = branches_to_show.len() > max_branches;
let branches_to_show: Vec<_> = branches_to_show.into_iter().take(max_branches).collect();
// Calculate commits ahead if requested
let commits_ahead_map: Option<HashMap<String, usize>> = if ahead {
Some(calculate_commits_ahead(
ctx,
target_oid.expect("target OID must exist when ahead calculation is enabled"),
&branches_to_show,
)?)
} else {
None
};
// Check merge status if requested
let merge_status_map: Option<HashMap<String, bool>> = if check_merge {
Some(check_branches_merge_cleanly(
ctx,
target_oid.expect("target OID must exist when merge check is enabled"),
&applied_stacks,
&branches_to_show,
)?)
} else {
None
};
View on GitHub (pinned to 2497b8007a)
Solutions
- Keep the resolution condition at list.rs:47 in sync with every flag that consumes target_oid
- Maintainer: replace the expects with ok_or_else errors naming the flag that required the target
- Add a CLI test matrix over --no-ahead, --no-check and --empty combinations
Example fix
// before
target_oid.expect("target OID must exist when ahead calculation is enabled")
// after
let target_oid = target_oid.ok_or_else(|| {
anyhow::anyhow!("--ahead needs a resolvable workspace target branch; run fetch/sync first")
})?; Defensive patterns
Strategy: validation
Validate before calling
// Lock the wiring: target resolution must cover every flag that needs the target
#[test]
fn branch_list_flags_never_panic_on_target() {
// exercises default (ahead+check on), --no-ahead, --no-check, --empty combos
for args in [["branch", "list"], ["branch", "list", "--no-ahead"], ["branch", "list", "--no-check", "--empty"]] {
env.but(&args).assert().success(); // or expected failure - but never a panic
}
} Prevention
- Whenever adding a flag that consumes target_oid, extend the resolution condition in the same commit
- Prefer propagating a descriptive error (ok_or_else) over expects for Option<OID> plumbing in CLI paths
- When users report 'target' failures, look for the propagated resolution error - the expect itself is a wiring guard
When it happens
Trigger: Only a refactor that resolves target_oid under a condition no longer implied by 'ahead' - for example adding a new mode that turns on ahead counting without updating the resolution condition at list.rs:47.
Common situations: Contributors adding flags that need (or deliberately skip) target resolution; not reachable by end users through valid CLI input.
Related errors
- target OID must exist when merge check is enabled
- non-empty conflicts map contains a commit
- object for prefix exists
- WithSyntaxHighlighting ensures the line is highlighted
- classified branches are guaranteed to be non-empty
AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17).
Data as JSON: /api/errors/3f3e6b5d1a8dadc1.
Report an issue: GitHub.