{"record":{"id":"036b9d917e5fbefd","repo":"gitbutlerapp/gitbutler","slug":"bug-we-have-to-avoid-using-these-legacy-types","errorCode":null,"errorMessage":"BUG: we have to avoid using these legacy types","messagePattern":"BUG: we have to avoid using these legacy types","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/gitbutler-branch-actions/src/base.rs","lineNumber":221,"sourceCode":"        None,\n    )?;\n\n    let project_meta = ProjectMeta {\n        target_ref: Some(target_branch_ref.to_string().try_into()?),\n        target_commit_id: Some(target_commit_oid),\n        push_remote: None,\n    };\n    project_meta.remote_url_with_fallback(&repo)?;\n\n    // TODO: make sure this is a real branch\n    let head_ref_name = current_head\n        .referent_name()\n        .context(\"Failed to get HEAD reference name\")?;\n    let head_is_workspace = head_ref_name == WORKSPACE_REF_NAME;\n    let head_name: Refname = head_ref_name\n        .to_string()\n        .parse()\n        .expect(\"BUG: we have to avoid using these legacy types\");\n    if workspace_ref_exists\n        && !head_is_workspace\n        && existing_target_ref.is_none_or(|target_ref| target_branch_ref != target_ref.as_ref())\n    {\n        bail_precondition!(\n            \"cannot change the target while HEAD is outside the GitButler workspace - return to workspace first\"\n        );\n    }\n    ctx.set_project_meta(project_meta)?;\n\n    let mut workspace_to_initialize = None;\n    if !head_is_workspace {\n        // if there are any commits on the head branch or uncommitted changes in the working directory, we need to\n        // put them into a virtual branch\n\n        let changes = but_core::diff::worktree_changes(&*ctx.repo.get()?)?.changes;\n        if !changes.is_empty() || current_head_commit != target_commit_oid {\n            let branch_matches_target = if let Refname::Local(head_name) = &head_name {","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/gitbutler-branch-actions/src/base.rs#L203-L239","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlet head_name: Refname = head_ref_name\n    .to_string()\n    .parse()\n    .expect(\"BUG: we have to avoid using these legacy types\");\n// after\nlet head_name: Refname = head_ref_name\n    .to_string()\n    .parse()\n    .context(\"Failed to parse HEAD reference name\")?;","handlingStrategy":"validation","validationCode":"// Before calling set_base_branch, check HEAD is on the workspace\nlet head = repo.head_name()?;\nif head.as_bstr() != WORKSPACE_REF_NAME.as_bytes() {\n    anyhow::bail!(\"HEAD is outside the GitButler workspace; return to workspace first\");\n}","typeGuard":"fn head_is_valid_refname(head_ref_name: &str) -> bool {\n    head_ref_name.parse::<Refname>().is_ok()\n}","tryCatchPattern":"// Callers should treat precondition failures as recoverable:\nmatch actions.set_base_branch(target) {\n    Err(err) if err.to_string().contains(\"outside the GitButler workspace\") => {\n        prompt_user_to_return_to_workspace();\n    }\n    other => other?,\n}","preventionTips":["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"],"tags":["rust","panic","git","legacy-types","refname"],"backgroundTag":"internal-invariant-violation","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}