This Git error occurs when trying to fetch a specific commit by its SHA hash, but the Git server is configured to only allow fetching advertised references like branches and tags. This commonly happens with submodules pointing to commits that don't exist on the remote or were removed during squash merges.
The "Server does not allow request for unadvertised object" error indicates that Git is trying to fetch a specific commit by its SHA hash directly, but the remote server refuses this request. Git servers maintain a list of "advertised" references (branches and tags) that clients can fetch. By default, most Git servers including GitHub and GitLab do not allow fetching arbitrary commits by their hash - only commits reachable from advertised refs. This restriction exists for security and performance reasons. Allowing direct object fetches could expose commits that were force-pushed away or exist in private forks. The error most commonly occurs in these scenarios: 1. **Submodule commit not pushed**: You updated a submodule, pushed the parent repository, but forgot to push the submodule itself. The parent references a commit that doesn't exist on the submodule's remote. 2. **Squash merge removed the commit**: When a pull request is squash-merged, the original commits are replaced with a single squash commit. If a submodule referenced one of those original commits, that reference becomes invalid. 3. **Shallow clone limitations**: When cloning with `--depth`, only recent commits are fetched. Submodules referencing older commits will fail to fetch. 4. **Force push removed the commit**: If someone force-pushed to the submodule repository, commits may have been removed from the remote.
The most common fix is to sync submodule URLs and update again. This resolves URL mismatches between .gitmodules and your local configuration:
# Sync submodule URLs from .gitmodules to .git/config
git submodule sync
# Update submodules recursively
git submodule update --init --recursiveIf this works, the issue was likely a stale submodule URL in your local configuration.
If syncing doesn't help, try forcing the update and using remote tracking branches instead of the specific commit:
# Force update using remote branch instead of recorded commit
git submodule update --force --recursive --init --remoteWarning: The --remote flag updates submodules to the latest commit on their remote tracking branch, not the commit recorded in the parent repository. This may change which version of the submodule you're using. After verifying things work, you may want to commit the new submodule reference:
git add .
git commit -m "Update submodule to latest"If you (or a teammate) updated the submodule but forgot to push it, the referenced commit won't exist on the remote. Navigate to the submodule and push:
# Go to the submodule directory
cd path/to/submodule
# Check the current state
git status
git log --oneline -5
# Push the submodule
git push origin HEAD
# Return to parent repo
cd ..
# Now update should work
git submodule update --init --recursiveTo prevent this in the future, use --recurse-submodules when pushing:
# Check submodules before push (fails if unpushed)
git push --recurse-submodules=check
# Automatically push submodules first
git push --recurse-submodules=on-demandIf you're working with a shallow clone (common in CI/CD), fetch the full history:
# Check if repository is shallow
git rev-parse --is-shallow-repository
# Returns "true" if shallow
# Fetch complete history
git fetch --unshallow
# Then update submodules
git submodule update --init --recursiveFor CI/CD pipelines, configure full clones:
GitHub Actions:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history
submodules: recursiveGitLab CI:
variables:
GIT_DEPTH: 0
GIT_SUBMODULE_STRATEGY: recursiveIf a pull request containing submodule updates was squash-merged, the original commit no longer exists. You need to update the submodule reference:
# Go to the submodule
cd path/to/submodule
# Find a valid commit (check the default branch)
git fetch origin
git log origin/main --oneline -10
# Checkout a valid commit
git checkout origin/main
# Go back to parent and update the reference
cd ..
git add path/to/submodule
git commit -m "Update submodule to valid commit after squash merge"To avoid this issue, configure your repository to use "merge commits" or "rebase and merge" instead of squash for PRs that update submodules.
As a last resort, you can remove and re-add the submodule. Note: This loses any local submodule changes.
# Remove the submodule
git submodule deinit -f path/to/submodule
rm -rf .git/modules/path/to/submodule
git rm -f path/to/submodule
# Re-add the submodule
git submodule add <repository-url> path/to/submodule
# Initialize and update
git submodule update --init --recursive
# Commit the change
git add .
git commit -m "Re-add submodule with valid reference"This creates a fresh submodule reference pointing to the current HEAD of the submodule's default branch.
If you control the Git server, you can enable fetching commits by SHA. This is NOT possible on GitHub or GitLab cloud.
For self-hosted Git servers, set these configuration options:
# On the Git server, enable SHA fetching
git config uploadpack.allowReachableSHA1InWant true
# Or allow any SHA (less restrictive)
git config uploadpack.allowAnySHA1InWant true
# For hidden refs as well
git config uploadpack.allowTipSHA1InWant trueSecurity warning: Enabling these options can expose commits that were intentionally hidden (force-pushed away, or in private forks). Only enable on private servers where this is acceptable.
### Understanding Advertised vs Unadvertised Objects
When you run git fetch, the server first sends a list of "advertised" references - branches and tags that the client is allowed to fetch. The client then requests objects reachable from these refs.
By default, Git servers only allow fetching objects reachable from advertised refs. This prevents:
- Fetching commits from private forks
- Accessing commits that were force-pushed away
- Retrieving objects that exist in the server's object store but aren't meant to be public
Submodules break this model because they record specific commit SHAs, not branch names. When that commit isn't at a branch tip, it becomes "unadvertised."
### Server Configuration Options
Three server-side options control SHA fetching:
| Option | Allows |
|--------|--------|
| uploadpack.allowTipSHA1InWant | Fetching SHAs at branch/tag tips |
| uploadpack.allowReachableSHA1InWant | Fetching any SHA reachable from a ref |
| uploadpack.allowAnySHA1InWant | Fetching any SHA in the object store |
GitHub and GitLab have allowReachableSHA1InWant enabled, but only for commits reachable from branches you have access to.
### Git Version Considerations
This error's behavior changed across Git versions:
- Git 2.5+: Added allowReachableSHA1InWant protocol support
- Git 2.11+: Improved shallow clone handling
- Git 2.22+: Some users reported new occurrences of this error due to protocol changes
If downgrading Git temporarily fixes the issue, it may indicate a protocol negotiation problem rather than a missing commit.
### Preventing This Error
1. Always push submodules first:
git push --recurse-submodules=on-demand2. Use branch tracking for submodules (Git 2.8+):
git config -f .gitmodules submodule.path.branch main
git submodule update --remote3. Avoid squash merges for submodule updates: Squashing removes the commits that submodules reference.
4. Document submodule workflow: Ensure all contributors know to push submodules before the parent repo.
### CI/CD Best Practices
For reliable submodule handling in CI/CD:
# GitHub Actions
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive
# If submodules still fail, add explicit sync
- run: |
git submodule sync --recursive
git submodule update --init --recursive --force### Diagnosing the Problem
To find which submodule and commit is causing the issue:
# Show submodule status
git submodule status --recursive
# Check what commit is expected
cat .gitmodules
git ls-tree HEAD path/to/submodule
# Verify if commit exists on remote
cd path/to/submodule
git fetch origin
git cat-file -t <commit-sha> # Should output "commit"
git branch -r --contains <commit-sha> # Should list branchesIf git branch -r --contains returns nothing, the commit exists locally but isn't reachable from any remote branch.
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