The 'fatal: git fetch-pack: expected shallow list' error occurs when Git's shallow clone protocol encounters an unexpected response during fetch operations. This typically happens in CI/CD pipelines where shallow clones lack required commit history for operations like merges, rebases, or version detection.
The "fatal: git fetch-pack: expected shallow list" error indicates a protocol-level failure during Git's fetch operation. This error occurs when Git expects to receive shallow clone metadata but the server or local repository state doesn't match expectations. Shallow clones are partial clones created with `git clone --depth=N` that only contain the most recent N commits. While they significantly reduce clone time and disk usage, they can cause problems when Git operations need access to commits beyond the shallow boundary. CI/CD systems commonly use shallow clones to speed up builds, but this optimization creates issues when: - A merge or rebase requires commits from before the shallow depth - Build tools try to count commits or determine versions from Git history - Git needs to find the merge-base between branches - The shallow clone's commit graph becomes inconsistent with the remote The error message "Shallow clone may not have all commits" explicitly warns that the requested operation cannot complete because the necessary commit history is unavailable in the shallow repository.
The most reliable fix is to convert the shallow clone to a full clone:
# Fetch all remaining history
git fetch --unshallow
# Verify the repository is no longer shallow
git rev-parse --is-shallow-repository
# Should output: falseIf --unshallow fails, try fetching with increased depth:
# Fetch more history incrementally
git fetch --depth=100
git fetch --depth=1000
git fetch --unshallowConfigure your CI/CD pipeline to clone with sufficient depth:
GitHub Actions:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history
# Or use a specific depth:
# fetch-depth: 100GitLab CI:
variables:
GIT_DEPTH: 0 # Full clone
# Or specific depth:
# GIT_DEPTH: 100
# Or per-job:
job_name:
variables:
GIT_DEPTH: 0Jenkins:
checkout([
$class: 'GitSCM',
extensions: [[$class: 'CloneOption', depth: 0, shallow: false]]
])CircleCI:
- checkout:
# CircleCI doesn't shallow clone by default
# But if using custom clone:
- run:
command: git clone --depth=0 $CIRCLE_REPOSITORY_URL .If full history is too expensive, fetch only what's needed:
# Fetch the target branch for merging
git fetch origin main:refs/remotes/origin/main
# Fetch with enough depth for merge base
git fetch --deepen=50 origin main
# Fetch all tags (often needed for version detection)
git fetch --tags
# Fetch specific commit if known
git fetch origin <commit-sha>For pull request workflows, fetch both branches:
# Fetch PR branch and target branch
git fetch origin pull/$PR_NUMBER/head:pr-branch
git fetch origin $TARGET_BRANCH:$TARGET_BRANCH --depth=100If the shallow clone metadata is corrupted, repair it:
# Check if shallow file exists and its contents
cat .git/shallow
# If corrupted or inconsistent, remove and re-fetch
rm .git/shallow
git fetch --unshallow
# Or recreate the repository
cd ..
rm -rf repo-name
git clone https://github.com/user/repo-name.gitIf you can't remove the repository (e.g., has local changes):
# Backup local changes
git stash
# Repair shallow information
git fetch origin --unshallow
# Restore changes
git stash popIf the remote was force-pushed, your shallow clone may reference commits that no longer exist:
# Reset to match remote state
git fetch origin
git reset --hard origin/main
# If that fails, start fresh
git fetch --refetch --unshallow
# Or clone again
cd ..
rm -rf repo-name
git clone https://github.com/user/repo-name.gitTo prevent this in CI, always use fresh clones:
# GitHub Actions - always clean
- uses: actions/checkout@v4
with:
clean: true
fetch-depth: 0Protocol version mismatches can cause shallow clone errors. Try switching versions:
# Use protocol version 2 (recommended)
git config --global protocol.version 2
# Or fall back to version 1 if v2 causes issues
git config --global protocol.version 1
# Then retry the operation
git fetch --unshallowIn CI/CD, set this before checkout:
- run: git config --global protocol.version 2
- uses: actions/checkout@v4Git 2.22+ supports partial clones, which are more flexible than shallow clones:
# Treeless clone - omits tree and blob objects
git clone --filter=tree:0 https://github.com/user/repo.git
# Blobless clone - omits blob objects
git clone --filter=blob:none https://github.com/user/repo.gitPartial clones include full commit history but lazily fetch file contents, avoiding the shallow clone limitations:
# GitHub Actions with partial clone
- uses: actions/checkout@v4
with:
filter: tree:0This approach maintains full history for merge operations while still reducing initial clone size.
If your build uses git describe for versioning, ensure tags are fetched:
# Fetch all tags
git fetch --tags --force
# If shallow, deepen to include tagged commits
git fetch --shallow-since="2023-01-01"
# Or deepen by count
git fetch --deepen=100
# Verify git describe works
git describe --tags --alwaysFor CI/CD:
# GitHub Actions
- uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
# GitLab CI
variables:
GIT_DEPTH: 0
GIT_STRATEGY: fetch
before_script:
- git fetch --tagsIf you only need the latest tag:
# Fetch annotated tags reachable from HEAD
git fetch --tags --shallow-exclude=$(git describe --tags --abbrev=0 2>/dev/null || echo HEAD~100)For very large repositories where full clones are impractical:
# Clone single branch with reasonable depth
git clone --depth=100 --single-branch --branch main https://github.com/user/repo.git
# For PRs, fetch only what's needed
git fetch --depth=100 origin main
git fetch --depth=100 origin pull/$PR_NUMBER/head:pr-branchUse sparse checkout to further reduce size:
git clone --filter=blob:none --sparse https://github.com/user/repo.git
cd repo
git sparse-checkout set path/to/needed/dirConfigure clone timeout to prevent hanging:
# GitLab CI
variables:
GIT_CLONE_TIMEOUT: 600 # seconds### Understanding Shallow Clone Internals
Shallow clones store their boundary commits in .git/shallow. This file lists the commits where history is truncated:
# View shallow boundaries
cat .git/shallow
# Output: one commit SHA per line
# Check if repository is shallow
git rev-parse --is-shallow-repositoryThe "expected shallow list" error occurs during the fetch-pack protocol negotiation when Git expects shallow information but receives something unexpected. This can happen when:
1. The .git/shallow file references commits that don't exist on the remote
2. Protocol negotiation fails due to version mismatch
3. The server doesn't support the shallow protocol correctly
### Shallow Clone vs Partial Clone
| Feature | Shallow Clone | Partial Clone |
|---------|--------------|---------------|
| Command | --depth=N | --filter=blob:none |
| History | Truncated | Complete |
| Merges | May fail | Work correctly |
| git log | Limited | Full |
| Lazy fetching | No | Yes |
| Git version | Any | 2.22+ |
### CI/CD Best Practices
For most CI/CD workflows, prefer these strategies in order:
1. For small/medium repos: Full clone (fetch-depth: 0)
2. For large repos with PR workflows: Partial clone (filter: blob:none)
3. For read-only builds: Shallow clone with sufficient depth (fetch-depth: 100)
Avoid shallow clones when:
- Build needs git describe for versioning
- PR workflows require merge/rebase
- Scripts traverse commit history
- Submodules need recursive fetch
### Debugging Protocol Issues
To diagnose fetch-pack errors:
# Verbose fetch
GIT_TRACE=1 GIT_TRACE_PACKET=1 git fetch --unshallow 2>&1 | head -100
# Check protocol version
git config --get protocol.version
# Test server capabilities
GIT_TRACE_PACKET=1 git ls-remote origin### GitHub Actions Specific Notes
GitHub's checkout action behavior:
- Default: fetch-depth: 1 (shallow)
- PR merges: fetches only PR head, not merge base
- Tags: not fetched by default
Recommended configuration for most projects:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: falseFor monorepos or very large repositories:
- uses: actions/checkout@v4
with:
filter: tree:0
fetch-depth: 0 # Still need full commit historykex_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