The 'git fetch --depth=50 origin +refs/pull/X/merge exited with 128' error occurs in CI/CD pipelines when Git cannot find the pull request merge reference. This typically happens when a PR is merged or closed before the build completes.
The "git fetch --depth=50 origin +refs/pull/X/merge: exited with 128" error indicates that Git failed with a fatal error (exit code 128) while trying to fetch a pull request merge reference from the remote repository. When CI/CD platforms like Travis CI, GitHub Actions, or Jenkins build pull requests, they fetch a special merge commit reference (refs/pull/X/merge) that represents the result of merging the PR branch into the base branch. This reference is temporary and managed by the Git hosting provider (GitHub, GitLab, Bitbucket). Exit code 128 in Git signifies a fatal error, and in this context, it means "fatal: Couldn't find remote ref refs/pull/X/merge". The merge reference doesn't exist because: - The PR was merged or closed, causing GitHub to delete the merge ref - There was a race condition between merging and the CI build starting - The PR branch was force-pushed, invalidating the previous merge ref - The repository's PR merge refs were not fetched due to shallow clone depth limitations
The most reliable prevention is to wait for all CI jobs to finish before clicking the merge button:
# In your .travis.yml, add branch protection awareness
branches:
only:
- main
- master
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/Best practices:
- Enable "Require status checks to pass before merging" in GitHub branch protection
- Wait for the green checkmark before merging
- Don't restart builds for already-merged PRs
If a PR was merged and you need to verify the build, trigger a build on the base branch instead:
Travis CI:
1. Go to your Travis CI dashboard
2. Navigate to the repository
3. Click "More options" > "Trigger build"
4. Select your base branch (main/master)
5. Click "Trigger custom build"
GitHub Actions:
If using GitHub Actions, the push event to main will automatically trigger a new build.
# Example workflow that builds on push to main
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]Sometimes increasing the clone depth helps when merge refs are further back in history:
# .travis.yml
git:
depth: 100 # Increase from default 50
# Or disable shallow cloning entirely (slower but more reliable)
git:
depth: falseFor GitHub Actions:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all historyFor GitLab CI:
variables:
GIT_DEPTH: 0 # Full cloneConfigure your CI to build the PR head commit instead of the merge commit:
# .travis.yml - build the PR branch directly
git:
depth: 50
# Optionally, configure to not build merge commits
# This is controlled by Travis settings, not YAMLGitHub Actions approach:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}This approach builds the PR branch tip rather than the merge result, which is more stable but doesn't test merge compatibility.
Before troubleshooting further, confirm the PR state:
# Check if the PR merge ref exists
gh pr view 123 --json state,merged
# Or using git directly
git ls-remote origin refs/pull/123/merge
git ls-remote origin refs/pull/123/headIf the PR is merged:
- The refs/pull/X/merge ref will not exist
- The refs/pull/X/head ref may still exist temporarily
If the PR is open and the ref doesn't exist, there may be a temporary GitHub issue.
Add error handling to your CI configuration for this specific scenario:
# .travis.yml with fallback
before_install:
- |
if ! git fetch origin +refs/pull/${TRAVIS_PULL_REQUEST}/merge: 2>/dev/null; then
echo "Merge ref not found, PR may have been merged"
if [ "${TRAVIS_PULL_REQUEST}" != "false" ]; then
echo "Building head commit instead"
git fetch origin +refs/pull/${TRAVIS_PULL_REQUEST}/head:
fi
fiGitHub Actions with conditional:
- name: Checkout with fallback
run: |
git fetch origin +refs/pull/${{ github.event.number }}/merge: || \
git fetch origin +refs/pull/${{ github.event.number }}/head:If the PR is still open and the error persists, check for platform issues:
Check status pages:
- GitHub: https://www.githubstatus.com/
- Travis CI: https://www.traviscistatus.com/
- GitLab: https://status.gitlab.com/
Manual retry steps:
1. Wait 5-10 minutes for any temporary issues to resolve
2. Push an empty commit to trigger a new build:
git commit --allow-empty -m "Trigger CI rebuild"
git push3. Or restart the build from the CI platform's web interface
Set up GitHub branch protection to prevent merging before CI completes:
1. Go to Settings > Branches in your GitHub repository
2. Click Add rule or edit existing rule for your main branch
3. Enable these options:
- Require status checks to pass before merging
- Require branches to be up to date before merging
- Select your CI jobs as required status checks
This prevents the race condition where someone merges while CI is still running.
# You can also use GitHub CLI to configure this
gh api repos/{owner}/{repo}/branches/main/protection -X PUT \
-F required_status_checks='{"strict":true,"contexts":["continuous-integration/travis-ci/pr"]}'### Understanding Pull Request Refs
GitHub maintains several refs for each pull request:
- refs/pull/X/head - Points to the tip of the PR branch
- refs/pull/X/merge - A temporary merge commit combining the PR with the base branch
The merge ref is particularly useful because it tests what the code will look like after merging. However, this ref is deleted when:
- The PR is merged (the ref becomes the actual merge commit on the base branch)
- The PR is closed without merging
- The base branch is updated and the merge ref needs regeneration
### Exit Code 128 Explained
Git exit code 128 indicates a fatal error. In this context:
- The fetch operation started successfully
- Git contacted the remote and requested the ref
- The server responded that the ref doesn't exist
- Git treats a missing requested ref as a fatal error
### Shallow Clone Considerations
CI platforms use --depth=50 (shallow clone) by default for performance:
# What Travis CI actually runs
git clone --depth=50 --branch=main https://github.com/user/repo.git
git fetch origin +refs/pull/123/merge:The depth parameter affects how much history is available locally but doesn't affect whether the merge ref exists on the remote.
### Alternative: GitLab Merge Request Refs
GitLab uses a different ref scheme:
- refs/merge-requests/X/head - The MR source branch
- refs/merge-requests/X/merge - The merge result (if merge is possible)
GitLab keeps merge refs available longer than GitHub in some cases.
### Debugging Ref Availability
To debug which refs are available:
# List all pull request refs
git ls-remote origin 'refs/pull/*'
# Check a specific PR
git ls-remote origin refs/pull/123/head refs/pull/123/merge
# In CI, log available refs
git fetch --dry-run origin '+refs/pull/*:refs/pull/*' 2>&1### Race Condition Timeline
Typical race condition scenario:
1. T+0s: Developer clicks merge button
2. T+1s: CI job starts (was queued)
3. T+2s: GitHub processes merge, deletes refs/pull/X/merge
4. T+5s: CI job tries to fetch refs/pull/X/merge
5. T+5s: Error - ref doesn't exist
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