{"record":{"id":"02527c8a2e6c5bac","repo":"gitbutlerapp/gitbutler","slug":"failed-to-determine-remote-for-non-remote-tracking","errorCode":null,"errorMessage":"failed to determine remote for non remote-tracking branch {target_ref}","messagePattern":"failed to determine remote for non remote-tracking branch (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-core/src/ref_metadata.rs","lineNumber":333,"sourceCode":"    /// remote behind [`Self::target_ref`].\n    ///\n    /// If no configured remote matches the target ref, fall back to the first path component\n    /// after `refs/remotes/`, the textual remote name that legacy metadata stored verbatim.\n    pub fn push_remote_name(&self, repo: &gix::Repository) -> Result<String> {\n        if let Some(name) = self.push_remote.clone() {\n            return Ok(name);\n        }\n        let target_ref = self.target_ref_or_err()?;\n        if let Some((remote_name, _short_name)) =\n            extract_remote_name_and_short_name(target_ref.as_ref(), &repo.remote_names())\n        {\n            return Ok(remote_name);\n        }\n        let (category, short_name) = target_ref\n            .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) =","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-core/src/ref_metadata.rs#L315-L351","documentation":"While deriving the push remote for a stack target, `push_remote_name()` falls back to the remote encoded in `target_ref`. It first tries configured remote names; if that fails it requires the ref's category to be `refs/remotes/...`. A target ref like `refs/heads/main` or any non-remote-tracking ref triggers this bail — the metadata simply has no remote to push to.","triggerScenarios":"Calling `push_remote_name(&repo)` on stack/target metadata whose `target_ref` is a local branch (`refs/heads/...`) while `push_remote` is unset — e.g. a project whose target was onboarded from a local branch, or legacy metadata storing a non-remote-tracking ref.","commonSituations":"Repository cloned without a remote or remote removed/renamed after onboarding (`origin` deleted); target set to a local branch during early GitButler setup; refs renamed via `git remote rename` so stored target refs no longer match `refs/remotes/<name>/...`.","solutions":["Set an explicit push remote in the stack/target metadata (`push_remote`) so the fallback path is never taken.","Re-add and fetch the remote so the target ref can be remote-tracking: `git remote add origin <url> && git fetch origin`, then re-onboard/re-target the stack to `refs/remotes/origin/<branch>`.","Check `git remote -v` and `git config --get-regexp '^remote\\.'` to confirm the remote the target ref's first component refers to actually exists.","If the remote was renamed, update the stored target ref to the new `refs/remotes/<new-name>/...`."],"exampleFix":"# before: target_ref = refs/heads/main (local branch, no remote)\n# after\ngit remote add origin git@github.com:org/repo.git\ngit fetch origin\ngit branch --set-upstream-to=origin/main main   # target becomes refs/remotes/origin/main","handlingStrategy":"validation","validationCode":"// Rust — ensure the target ref is remote-tracking (or push_remote set) before pushing\nfn push_remote_resolvable(\n    meta: &but_core::ref_metadata::StackMetadata,\n    repo: &gix::Repository,\n) -> bool {\n    meta.push_remote.is_some()\n        || meta\n            .target_ref\n            .as_ref()\n            .and_then(|r| {\n                let names = repo.remote_names();\n                gix::refs::transaction::RefLogAngle::default(); // keep clippy quiet in snippets\n                but_core::ref_metadata::extract_remote_name_and_short_name(r.as_ref(), &names)\n                    .map(|(name, _)| names.contains(&name))\n            })\n            .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"let remote_name = match meta.push_remote_name(&repo) {\n    Ok(name) => name,\n    Err(err) if err.to_string().contains(\"non remote-tracking branch\") => {\n        // metadata target is a local branch: ask the user to pick a remote-tracking target\n        // or configure push_remote, instead of surfacing a raw error\n        prompt_user_to_set_push_remote(&meta)?\n    }\n    Err(err) => return Err(err),\n};","preventionTips":["Require a fetched remote at project onboarding time so target refs are always `refs/remotes/<remote>/...`.","Set an explicit `push_remote` in metadata when the remote name ever differs from the target's first path component.","After `git remote rename`, update stored target refs and push_remote values."],"tags":["git-remote","ref-metadata","push","git-config"],"backgroundTag":"git-remote-not-configured","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}