The 'fatal: Couldn't find remote ref refs/heads/branch-name' error occurs when Git cannot locate the specified branch on the remote repository. This typically happens when a branch has been deleted, renamed, or never existed on the remote, often surfacing in CI/CD pipelines attempting to checkout stale branch references.
The "fatal: Couldn't find remote ref refs/heads/branch-name" error indicates that Git attempted to fetch or checkout a branch from the remote repository, but that branch does not exist there. The refs/heads/ prefix refers to branches (as opposed to refs/tags/ for tags), and Git is explicitly stating it cannot find the specified branch reference. This error commonly appears in CI/CD environments where pipeline configurations reference branches that have been deleted, merged, or renamed since the configuration was written. It can also occur when a developer tries to checkout a branch that was only created locally by a teammate and never pushed to the remote. The error is particularly common after branch cleanup operations, pull request merges with automatic branch deletion enabled, or when switching between repositories that have different branch structures.
First, check which branches actually exist on the remote repository:
# Fetch the latest remote info
git fetch origin
# List all remote branches
git branch -r
# Or list all branches (local and remote)
git branch -aIf your target branch is not in the list, it either never existed or has been deleted. You can also check directly on GitHub/GitLab web interface to confirm.
To see when branches were deleted (if you have access to reflog):
git reflog show --all | grep "branch-name"Branch names are case-sensitive on most Git hosting platforms. Verify the exact spelling:
# List remote branches and search for similar names
git branch -r | grep -i "branch"
# Check if using different casing
git branch -r | grep -i "feature"Common mistakes include:
- Feature/login vs feature/login
- develop vs development
- master vs main
If you find the correct name, update your command or CI configuration accordingly.
Your local repository might have stale references to branches that no longer exist. Prune them:
# Prune deleted remote branches
git fetch --prune
# Or prune specifically from origin
git remote prune origin
# Verify the cleanup
git branch -rAfter pruning, if you still need to reference a branch that was deleted, you'll need to recreate it or update your workflow to use an existing branch.
If this error occurs in CI/CD, update the pipeline configuration to reference an existing branch.
GitHub Actions:
# .github/workflows/ci.yml
on:
push:
branches:
- main # Update from old branch name
- develop
pull_request:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: main # Ensure this branch existsGitLab CI:
# .gitlab-ci.yml
default:
# Remove hardcoded branch references if not needed
deploy:
script:
- echo "Deploying..."
only:
- main # Update to existing branchJenkins:
// Jenkinsfile
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git branch: 'main', // Update branch name
url: 'https://github.com/user/repo.git'
}
}
}
}If your repository's default branch was renamed, update all references:
# Update your local default branch
git branch -m master main
git fetch origin
git branch -u origin/main main
git remote set-head origin -a
# Verify the change
git remote show originUpdate any CI/CD configurations, deployment scripts, or documentation that references the old branch name.
To find files referencing old branch names:
# Search for 'master' references in config files
grep -r "master" .github/ .gitlab-ci.yml Jenkinsfile 2>/dev/nullIf you need to restore a deleted branch, you may be able to recover it:
# If you have the branch locally
git push origin branch-name
# If you know the last commit hash
git checkout -b branch-name <commit-hash>
git push origin branch-nameOn GitHub, recently deleted branches from merged PRs can often be restored via the web interface:
1. Go to the closed Pull Request
2. Look for "Restore branch" button
If the branch was never pushed, you'll need to get it from whoever created it locally.
If the error occurs during submodule operations, update the submodule configuration:
# Check submodule status
git submodule status
# View submodule configuration
cat .gitmodulesUpdate the branch in .gitmodules:
[submodule "libs/mylib"]
path = libs/mylib
url = https://github.com/user/mylib.git
branch = main # Update to existing branchThen sync the changes:
git submodule sync
git submodule update --init --recursiveTo avoid hardcoding branch names in scripts, detect the default branch programmatically:
# Get the default branch name from remote
DEFAULT_BRANCH=$(git remote show origin | grep "HEAD branch" | cut -d ":" -f 2 | xargs)
# Use it in your commands
git checkout "$DEFAULT_BRANCH"
git pull origin "$DEFAULT_BRANCH"In GitHub Actions:
- name: Get default branch
run: |
DEFAULT_BRANCH=$(git remote show origin | grep "HEAD branch" | cut -d ":" -f 2 | xargs)
echo "DEFAULT_BRANCH=$DEFAULT_BRANCH" >> $GITHUB_ENV
- name: Checkout default
run: git checkout ${{ env.DEFAULT_BRANCH }}This approach is more resilient to branch renames.
### Understanding Git Refs
Git references (refs) are pointers to commits. The refs/heads/ namespace contains branches:
- refs/heads/main - the main branch
- refs/tags/v1.0.0 - a tag
- refs/remotes/origin/main - remote tracking branch
When you see "Couldn't find remote ref refs/heads/branch-name", Git looked in the remote's refs/heads/ namespace and found nothing matching "branch-name".
### Shallow Clone Considerations
CI/CD systems often use shallow clones for speed, which can cause ref-related issues:
# Shallow clone only fetches limited history
git clone --depth 1 https://github.com/user/repo.git
# If you need to checkout a different branch
git fetch --unshallow
git fetch origin other-branch
git checkout other-branchOr specify the branch during shallow clone:
git clone --depth 1 --branch main https://github.com/user/repo.git### Checking Remote Without Cloning
To verify a branch exists before attempting operations:
# List remote refs without cloning
git ls-remote --heads origin
# Check for specific branch
git ls-remote --heads origin branch-name
# Returns nothing if branch doesn't exist
# Returns ref and hash if it doesThis is useful in scripts to validate before attempting checkout.
### Common CI/CD Gotchas
GitHub Actions with pull_request events:
The GITHUB_REF for PRs points to refs/pull/123/merge, not the source branch. If your workflow assumes a branch name, it may fail.
Forked repository PRs:
When a PR comes from a fork, the source branch exists in the fork, not the main repository. Checkout requires special handling:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}### Debugging Ref Issues
Enable Git tracing to see exactly what Git is doing:
GIT_TRACE=1 GIT_CURL_VERBOSE=1 git fetch origin branch-nameThis shows the HTTP requests and responses, helping identify if the branch exists but is inaccessible vs. truly missing.
### Protected and Hidden Branches
Some Git hosting platforms allow hiding branches from normal listing:
- GitLab protected branches may have visibility restrictions
- GitHub branch protection doesn't hide branches but may prevent operations
- Some enterprise setups use branch namespacing that affects visibility
Check repository settings if a branch should exist but doesn't appear in listings.
kex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server
fatal: unable to access: Proxy auto-configuration failed
How to fix 'Proxy auto-configuration failed' in Git
fatal: unable to access: Authentication failed (proxy requires basic auth)
How to fix 'Authentication failed (proxy requires basic auth)' in Git
fatal: unable to access: no_proxy configuration not working
How to fix 'no_proxy configuration not working' in Git
fatal: unable to read tree object in treeless clone
How to fix 'unable to read tree object in treeless clone' in Git