Overview
The error “fatal: HEAD is not a valid object” means Git cannot resolve the name HEAD to a commit. This happens when a repository has no commits yet, when HEAD points to a branch that does not exist, or when refs are corrupted/missing (common in CI, bare repos, or after manual edits in .git).
Exact wording varies by Git version and command. You might also see:
- fatal: bad object HEAD
- fatal: ambiguous argument 'HEAD'
The fixes below cover all common DevOps/Git scenarios.
Quickstart (most common fixes)
- New repo with no commits: create the initial commit.
- Default branch missing/renamed: point HEAD to an existing branch.
- Broken HEAD file: recreate HEAD as a symbolic ref.
- Repo missing local commits but has a remote: fetch and reset to a remote branch.
Minimal commands:
# Case A: No commits yet
git add -A
git commit -m "Initial commit"
# OR create an empty initial commit
git commit --allow-empty -m "Initial commit"
# Case B: HEAD points to a non-existent branch; set to main (or your branch)
git symbolic-ref HEAD refs/heads/main
# Case C: Recover from remote
git fetch --all --prune
git checkout -B main origin/main
Minimal working example (reproduce and fix)
Reproduce a broken HEAD referencing a missing branch:
mkdir demo && cd demo
git init
# Make a real commit so HEAD resolves initially
printf "hello\n" > file.txt
git add file.txt
git commit -m "init"
# Create main, then point HEAD to it
git branch -M main
# Simulate a broken ref: delete the branch ref
rm -f .git/refs/heads/main
# Now HEAD cannot resolve to a commit
# Many commands will fail similarly
( git rev-parse --verify HEAD ) || echo "HEAD is broken"
# Fix: point HEAD to an existing commit by recreating the branch
last=$(git show-ref -s --heads | head -n1)
# If none, fetch a remote or create a new commit
git update-ref refs/heads/main "$last" || git commit --allow-empty -m "re-anchor"
git symbolic-ref HEAD refs/heads/main
# Verify
git rev-parse --verify HEAD
git status
Notes:
- Depending on your Git version, errors may read “fatal: HEAD is not a valid object,” “fatal: bad object HEAD,” or “ambiguous argument 'HEAD'.”
- In CI/bare repos, prefer the simpler recovery recipes in the next section.
Diagnose before fixing
- Check if HEAD resolves:
git rev-parse --verify HEAD- Exit code 0: HEAD is fine. The error came from a different cause/command.
- Non‑zero: proceed.
- List local branches/refs:
git show-ref --heads git branch -vv - Check the HEAD file:
Expect:cat .git/HEADref: refs/heads/main(or your default branch). If it’s missing or not aref:line, it’s broken. - If you have a remote:
git remote -v git ls-remote --heads origin - Confirm if there are zero commits:
git log --oneline -1 || echo "No commits yet"
Fix by scenario
A) Repository has no commits yet
Symptom: new repo, git log says “your current branch ... does not have any commits yet,” or HEAD resolution fails.
Fix:
git add -A
git commit -m "Initial commit"
# or
git commit --allow-empty -m "Initial commit"
B) HEAD points to a missing/renamed branch
Symptom: .git/HEAD contains ref: refs/heads/main but refs/heads/main does not exist (branch renamed or deleted).
Fix (choose one):
- Point HEAD to an existing branch:
git symbolic-ref HEAD refs/heads/master # if master exists - Recreate the branch from the latest known commit:
last=$(git show-ref -s --heads | head -n1) git update-ref refs/heads/main "$last" git symbolic-ref HEAD refs/heads/main - If you have a remote default branch:
git fetch --all --prune git checkout -B main origin/main
C) Broken or missing HEAD file
Symptom: .git/HEAD missing or contains a raw commit hash (not a symbolic ref) when you expect a branch.
Fix (set HEAD to your default branch):
echo "ref: refs/heads/main" > .git/HEAD
# If refs/heads/main is absent, create it from a commit or remote.
D) Detached HEAD pointing at a missing commit
Symptom: you were in detached HEAD and the object was garbage-collected or never fetched.
Fix:
git fetch --all --prune
# Reattach to a branch that exists on the remote
git checkout -B main origin/main
E) Bare repositories (servers, CI caches)
Symptom: bare repo with incorrect default branch pointer.
Fix:
# In the bare repo directory (contains HEAD and objects but no working tree)
git symbolic-ref HEAD refs/heads/main
Verification checklist
git rev-parse --verify HEADreturns a commit ID.git statusshows a branch name (not detached unless intentionally).git log -1 --onelineprints the latest commit.
Pitfalls and safety
- Destructive commands:
git reset --harddiscards local changes. Avoid using it as a “fix.”- Editing files in .git can corrupt refs if mistaken. Prefer
git symbolic-refandgit update-ref.
- Branch names:
- Match your environment (main vs master). Don’t assume.
- CI considerations:
- Ensure the checkout step fetches the target ref. For shallow clones, include the branch commit you need (e.g.,
fetch-depth: 0).
- Ensure the checkout step fetches the target ref. For shallow clones, include the branch commit you need (e.g.,
- Remote mismatch:
- If the default branch changed, update HEAD or your checkout scripts accordingly.
Performance notes
git fetch --all --prunecan be network‑heavy; fetch only the branch you need:git fetch origin main:maingit fsckis thorough but slow on large repos. Use it only when corruption is suspected.- To resolve HEAD quickly without scanning all refs, jump directly to a known commit or remote ref (e.g.,
origin/main). - Avoid repeated
git gcin CI; rely on scheduled maintenance instead.
Tiny FAQ
Q: Why does this happen right after
git init? A: There’s no commit yet; create the initial commit so HEAD can point to one.Q: Our default branch changed from master to main. What now? A: Update HEAD:
git symbolic-ref HEAD refs/heads/main, and ensure the branch exists locally or fetch it.Q: Can I just run
git reset --hard HEADto fix it? A: No. If HEAD is invalid, that command fails and may hide the real issue. Fix HEAD first.Q: Is my history lost? A: Usually no. If commits exist in the remote or other refs, fetch/reset to them. Only missing objects combined with expired reflogs risk data loss.
Q: How do I detect this early in CI? A: Add a guard step:
git rev-parse --verify HEAD || exit 1and fix the checkout or HEAD ref when it fails.