Overview
The Git error "fatal: early EOF" usually means the connection closed before all data was received. It often appears with messages like "index-pack failed" or "The remote end hung up unexpectedly." This guide covers pragmatic fixes for CI and local DevOps workflows.
Quickstart: try a robust clone
If you just need a reliable clone on a flaky network or behind a strict proxy, try:
# Minimal working example: resilient shallow clone over HTTPS
URL="https://example.com/owner/repo.git"
git -c http.version=HTTP/1.1 \
-c http.maxRequests=1 \
-c http.lowSpeedLimit=1 \
-c http.lowSpeedTime=600 \
-c protocol.version=2 \
clone --depth=1 --single-branch --no-tags "$URL" repo
- HTTP/1.1 and maxRequests=1 avoid some HTTP/2/proxy quirks.
- lowSpeedTime gives slow links more time before aborting.
- Shallow clone reduces transferred data.
If it works, deepen later with git fetch --unshallow.
Why it happens
Common causes:
- Intermittent network or aggressive corporate proxies closing idle streams.
- HTTP/2 issues with middleboxes; multiplexed requests can be dropped.
- Large repos; long transfer times trigger timeouts.
- Antivirus or filesystem filters disrupting pack-file writes (often on Windows).
- Git LFS objects not handled due to missing LFS client.
- Server-side timeouts or resource limits.
Step-by-step troubleshooting
Follow in order; stop when the problem is resolved.
- Verify connectivity and URL
- Test reachability:
# HTTPS
curl -I https://example.com/owner/repo.git/info/refs?service=git-upload-pack
# SSH
ssh -T [email protected] || true
- Fix DNS/VPN/proxy first if these fail.
- Switch protocol (HTTPS ↔ SSH)
- Try the other protocol to bypass proxy quirks.
# From HTTPS to SSH
git clone [email protected]:owner/repo.git
- For SSH stability on long transfers, add keepalives:
# ~/.ssh/config
Host example.com
ServerAliveInterval 30
ServerAliveCountMax 10
- Reduce transferred data (shallow, single branch, no tags)
git clone --depth=1 --single-branch --no-tags https://example.com/owner/repo.git
- Later, selectively deepen:
git fetch --deepen=200
- Tame HTTP behavior
- Force HTTP/1.1 and serialize requests (helps with strict proxies):
git config --global http.version HTTP/1.1
git config --global http.maxRequests 1
- Allow slow links more time before aborting:
git config --global http.lowSpeedLimit 1
git config --global http.lowSpeedTime 600
- Ensure protocol v2 is enabled (more efficient fetch negotiation):
git config --global protocol.version 2
- Diagnose with verbose tracing
- Enable curl and packet traces to pinpoint where it dies:
GIT_CURL_VERBOSE=1 GIT_TRACE_PACKET=1 GIT_TRACE=1 \
git fetch --prune --progress origin
- Look for proxy resets, HTTP/2 errors, or LFS URLs.
- Handle Git LFS objects
- If LFS pointers are involved, install and fetch via LFS:
git lfs install
git lfs fetch --all
git lfs checkout
- Avoid local interference (Windows/macOS)
- Exclude your repo directory from antivirus/endpoint scanning.
- On Windows, ensure long paths are enabled:
git config --global core.longpaths true
- Clean and retry
- Remove partial clone dir and retry with resilient settings:
rm -rf repo
git -c http.version=HTTP/1.1 -c http.maxRequests=1 \
-c http.lowSpeedLimit=1 -c http.lowSpeedTime=600 \
clone --depth=1 https://example.com/owner/repo.git repo
- Server-side considerations (contact admin if applicable)
- Increase idle timeouts on reverse proxies.
- Ensure Git HTTP backend supports protocol v2.
- Check repo health; run
git gcserver-side.
Decision guide
| Symptom | Likely cause | Fix |
|---|---|---|
| Dies after long idle | Proxy/idle timeout | Force HTTP/1.1, increase lowSpeedTime, SSH keepalive |
| Fails only over HTTPS | HTTP/2/proxy issue | http.version=HTTP/1.1, maxRequests=1 |
| Large repo, slow link | Transfer too big | Shallow clone, single branch, no tags |
| Mentions LFS | Missing LFS client | Install Git LFS, run lfs fetch |
| Windows-only | AV/FS filters | Exclude repo, core.longpaths |
Performance notes
- Shallow clones are fastest but limit history-dependent operations; deepen incrementally as needed.
- HTTP/1.1 may be slower than HTTP/2 where proxies are friendly; prefer HTTP/2 in clean environments.
- Reducing
http.maxRequeststo 1 improves reliability through strict proxies but can reduce throughput. - Protocol v2 reduces round trips and bytes; keep it enabled.
- Caching dependencies or using a nearby mirror dramatically lowers failures in CI.
Pitfalls to avoid
- Do not rely on
http.postBuffer: it affects pushes and is ignored for fetches in modern Git. - Avoid permanently disabling SSL verification; use only for narrow debugging if ever, then revert.
- Don’t keep extreme
lowSpeedTimesettings globally if not needed; they can mask genuine stalls. - Shallow clones can break tooling that expects full history; plan a deepen step when required.
Minimal robust clone script (CI-friendly)
Use this snippet to retry a resilient shallow clone up to 3 times before failing.
set -euo pipefail
URL="$1"; DIR="${2:-repo}"
for i in 1 2 3; do
rm -rf "$DIR"
if git -c http.version=HTTP/1.1 \
-c http.maxRequests=1 \
-c http.lowSpeedLimit=1 \
-c http.lowSpeedTime=600 \
-c protocol.version=2 \
clone --depth=1 --single-branch --no-tags "$URL" "$DIR"; then
echo "Clone succeeded on attempt $i"; exit 0
fi
echo "Clone failed (attempt $i). Retrying..." >&2
sleep 5
done
echo "Clone failed after 3 attempts" >&2
exit 1
Tiny FAQ
- Q: I see "fatal: early EOF" and "index-pack failed". Related?
- A: Yes. The pack download or unpack didn’t finish; fix the underlying transfer issue.
- Q: Will increasing
http.lowSpeedTimealways help?- A: It helps slow links but won’t fix hard disconnects or proxy resets.
- Q: Is
http.postBuffera fix?- A: No. It’s for pushes and is ineffective for clone/fetch in modern Git.
- Q: Should I switch to SSH?
- A: Often yes, especially behind proxies that meddle with HTTPS. Add keepalives.
- Q: How do I confirm it’s a proxy issue?
- A: Traces showing HTTP/2 stream resets or connections closing mid-transfer are strong indicators; testing from a different network can confirm.