{"record":{"id":"71cd7783f23f33cc","repo":"gitbutlerapp/gitbutler","slug":"target-ref-required-for-remote-url","errorCode":null,"errorMessage":"Target ref required for remote url","messagePattern":"Target ref required for remote url","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-core/src/ref_metadata.rs","lineNumber":348,"sourceCode":"            .category_and_short_name()\n            .with_context(|| format!(\"failed to determine remote for branch {target_ref}\"))?;\n        if category != gix::refs::Category::RemoteBranch {\n            bail!(\"failed to determine remote for non remote-tracking branch {target_ref}\");\n        }\n        let slash_pos = short_name.find_byte(b'/').with_context(|| {\n            format!(\"remote tracking branch {target_ref} didn't have '/' in its short name\")\n        })?;\n        let remote_name = short_name[..slash_pos].to_str_lossy().into_owned();\n        tracing::warn!(\n            \"remote '{remote_name}' of target ref {target_ref} is not configured in git config\"\n        );\n        Ok(remote_name)\n    }\n\n    /// Get the fetch URL of the remote behind [`Self::target_ref`].\n    pub fn remote_url_with_fallback(&self, repo: &gix::Repository) -> Result<String> {\n        let Some(target_ref) = self.target_ref.as_ref() else {\n            bail!(\"Target ref required for remote url\")\n        };\n        let remote_names = repo.remote_names();\n        let (remote_name, _short_name) =\n            extract_remote_name_and_short_name(target_ref.as_ref(), &remote_names).context(\n                format!(\"failed to determine remote for branch {target_ref}\"),\n            )?;\n        let remote = repo.find_remote(remote_name.as_str()).context(format!(\n            \"failed to find remote {remote_name} for branch {target_ref}\"\n        ))?;\n        remote\n            .url(gix::remote::Direction::Fetch)\n            .map(|url| url.to_bstring().to_string())\n            .context(format!(\"failed to get fetch url for remote {remote_name}\"))\n    }\n\n    /// The URL to push to, inferred by the [`Self::push_remote`] property.\n    ///\n    /// Falls back to the fetch URL of the remote behind [`Self::target_ref`] if","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-core/src/ref_metadata.rs#L330-L366","documentation":"`remote_url_with_fallback()` derives a fetch URL from the workspace/stack's `target_ref`; if that field is `None` it bails immediately with 'Target ref required for remote url'. Unlike sibling methods (`target_ref_or_err`) it does not attach a `DefaultTargetNotFound` code — it's a plain precondition failure: the metadata was never populated with a target.","triggerScenarios":"Calling `remote_url_with_fallback(&repo)` on metadata whose `target_ref` is None — a workspace or stack that hasn't completed onboarding, an entry created but never targeted, or a caller holding partially-initialized ref metadata.","commonSituations":"Projects added but never assigned a target branch; tests constructing ref metadata by hand without a target; code paths that run right after workspace creation before the first fetch.","solutions":["Guard the call: only ask for the remote URL when `target_ref` is present (use `target_ref_or_err()`-style checks to surface a proper DefaultTargetNotFound to the UI).","Complete onboarding so the target ref is set before any URL-dependent feature (push, forge integration) runs.","If the target was lost, re-select the target branch in GitButler to repopulate `target_ref`."],"exampleFix":"// before\nlet url = meta.remote_url_with_fallback(&repo)?;\n\n// after\nlet url = meta\n    .target_ref\n    .as_ref()\n    .map(|_| meta.remote_url_with_fallback(&repo))\n    .transpose()?\n    .unwrap_or_else(|| fallback_url.clone());","handlingStrategy":"validation","validationCode":"// Rust — only ask for the remote URL when a target ref exists\nif meta.target_ref.is_none() {\n    // skip URL-dependent features; complete onboarding instead of erroring\n    return Ok(UserAction::SelectTargetBranch);\n}\nlet url = meta.remote_url_with_fallback(&repo)?;","typeGuard":"fn has_target_ref(meta: &but_core::ref_metadata::StackMetadata) -> bool {\n    meta.target_ref.is_some()\n}","tryCatchPattern":"match meta.remote_url_with_fallback(&repo) {\n    Ok(url) => url,\n    Err(err) if err.to_string().contains(\"Target ref required\") => {\n        fallback_url.clone() // or trigger target selection flow\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Gate push/forge features on target_ref presence during onboarding flows.","Prefer `target_ref_or_err()` at UI boundaries so users get a DefaultTargetNotFound-style prompt rather than a raw error.","Don't construct partial ref metadata in tests without a target ref unless testing this path."],"tags":["ref-metadata","precondition","remote-url","gitbutler"],"backgroundTag":"missing-target-ref","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}