gitbutlerapp/gitbutler · error
BUG: we have to avoid using these legacy types
Error message
BUG: we have to avoid using these legacy types
What it means
In set_base_branch, the raw HEAD reference name string is parsed into a legacy `Refname` type and the parse result is unwrapped with expect. The developer marked this as a bug-prone legacy dependency: the codebase is migrating away from the legacy Refname parsing, and any HEAD ref name that does not parse as a Refname panics. It signals tech debt rather than a user-facing validation error.
Solutions
- Return to the GitButler workspace (checkout the workspace ref) before changing the target branch — the function already bail_precondition! when HEAD is outside the workspace
- Refactor to the modern ref name types (gix::refs::FullName / Refname from gitbutler-refname) and return a Result instead of expect
- Check `git symbolic-ref HEAD` / status before the operation to ensure HEAD is attached to a valid branch
Example fix
// before
let head_name: Refname = head_ref_name
.to_string()
.parse()
.expect("BUG: we have to avoid using these legacy types");
// after
let head_name: Refname = head_ref_name
.to_string()
.parse()
.context("Failed to parse HEAD reference name")?; Defensive patterns
Strategy: validation
Validate before calling
// Before calling set_base_branch, check HEAD is on the workspace
let head = repo.head_name()?;
if head.as_bstr() != WORKSPACE_REF_NAME.as_bytes() {
anyhow::bail!("HEAD is outside the GitButler workspace; return to workspace first");
} Type guard
fn head_is_valid_refname(head_ref_name: &str) -> bool {
head_ref_name.parse::<Refname>().is_ok()
} Try / catch
// Callers should treat precondition failures as recoverable:
match actions.set_base_branch(target) {
Err(err) if err.to_string().contains("outside the GitButler workspace") => {
prompt_user_to_return_to_workspace();
}
other => other?,
} Prevention
- Always return to the GitButler workspace before switching the target branch
- Avoid detached HEAD or unusual refs while using workspace operations
- Migrate call sites to modern ref-name types that return Results instead of parsing legacy Refname with expect
When it happens
Trigger: Calling set_base_branch (changing the GitButler workspace target branch) when HEAD points at a ref whose name cannot be parsed into the legacy Refname type, e.g. HEAD detached or on an unusual ref name.
Common situations: User is in detached-HEAD state or on a ref with an unexpected prefix; HEAD points at something other than the workspace ref or a normal branch while attempting to change the target; legacy/new ref-type migration left inconsistent state.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- BUG: value must be valid ref name
- no illformed UTF8
- repo access failed
- valid commit that can be parsed: TODO - allow it to return…
- write to memory succeeds
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/036b9d917e5fbefd.
Report an issue: GitHub.
Appendix: source
Thrown at crates/gitbutler-branch-actions/src/base.rs:221
None,
)?;
let project_meta = ProjectMeta {
target_ref: Some(target_branch_ref.to_string().try_into()?),
target_commit_id: Some(target_commit_oid),
push_remote: None,
};
project_meta.remote_url_with_fallback(&repo)?;
// TODO: make sure this is a real branch
let head_ref_name = current_head
.referent_name()
.context("Failed to get HEAD reference name")?;
let head_is_workspace = head_ref_name == WORKSPACE_REF_NAME;
let head_name: Refname = head_ref_name
.to_string()
.parse()
.expect("BUG: we have to avoid using these legacy types");
if workspace_ref_exists
&& !head_is_workspace
&& existing_target_ref.is_none_or(|target_ref| target_branch_ref != target_ref.as_ref())
{
bail_precondition!(
"cannot change the target while HEAD is outside the GitButler workspace - return to workspace first"
);
}
ctx.set_project_meta(project_meta)?;
let mut workspace_to_initialize = None;
if !head_is_workspace {
// if there are any commits on the head branch or uncommitted changes in the working directory, we need to
// put them into a virtual branch
let changes = but_core::diff::worktree_changes(&*ctx.repo.get()?)?.changes;
if !changes.is_empty() || current_head_commit != target_commit_oid {
let branch_matches_target = if let Refname::Local(head_name) = &head_name {View on GitHub (pinned to 58e5313667)