KhueApps
Home/DevOps/Fix Git error: fatal: bad object HEAD

Fix Git error: fatal: bad object HEAD

Last updated: October 07, 2025

What this error means

Git resolves HEAD to the current commit. The error fatal: bad object HEAD means Git cannot resolve HEAD to a valid commit object. Common causes:

  • Unborn branch: repository has no commits yet.
  • Broken symbolic ref: .git/HEAD points to a branch that does not exist.
  • Missing branch ref: refs/heads/<branch> points to a non-existent commit.
  • Corruption or incomplete clone: required objects are missing locally.
  • After history rewrite: local branch ref points to a commit that was garbage-collected or never fetched.

Quickstart fixes

Use these in the suspected repository directory.

  • Check current state:
    • git status
    • git rev-parse --abbrev-ref HEAD
    • cat .git/HEAD
  • If this is a brand-new repo (no commits):
    • git commit --allow-empty -m "Initial commit"
  • If .git/HEAD refers to a missing branch (e.g., refs/heads/main):
    • git fetch origin
    • git symbolic-ref HEAD refs/heads/main
    • git reset --hard origin/main
  • If you know a good commit hash to restore a branch:
    • git update-ref refs/heads/main <commit>
    • git symbolic-ref HEAD refs/heads/main
  • If you suspect corruption or missing objects:
    • git fetch --all --prune --tags
    • git fsck --full
    • If unresolved, reclone or restore from a known-good remote/backup.

Minimal working example (reproduce and fix)

The script below simulates a broken branch ref, triggers the error, then fixes it.

# Create a temporary repo
set -euo pipefail
WORKDIR=$(mktemp -d)
cd "$WORKDIR"

# Initialize a repo with a 'main' branch
if git init -b main 2>/dev/null; then :; else git init && git checkout -b main; fi

echo hello > file.txt
git add file.txt
git commit -m "init"

# Record the current good commit
GOOD=$(git rev-parse HEAD)

# Break the branch ref (simulate missing refs/heads/main)
rm -f .git/refs/heads/main

# Commands referencing HEAD now fail
set +e
git rev-parse HEAD
# -> fatal: bad object HEAD
set -e

# Fix: restore the branch ref to the last known good commit
git update-ref refs/heads/main "$GOOD"
# Ensure HEAD points to that branch
git symbolic-ref HEAD refs/heads/main

# Verify
git rev-parse HEAD
git log --oneline -1

Result: the error disappears and HEAD is resolvable again.

Step-by-step diagnosis and repairs

  1. Inspect HEAD and branches
  • cat .git/HEAD
    • Expected: ref: refs/heads/<branch>
    • If it is a raw hash or empty, HEAD is corrupt; reset it with git symbolic-ref HEAD refs/heads/<branch>.
  • List branch refs: git show-ref --heads
  • List branches: git branch -a
  1. Unborn branch (no commits yet)

Symptoms:

  • git status shows “No commits yet on <branch>”.
  • git log fails and HEAD is unresolved in some commands.

Fix:

  • Create the first commit:
    • git commit --allow-empty -m "Initial commit"
    • Or add a file, then git add . && git commit -m "Initial commit"
  1. .git/HEAD points to a non-existent branch

Symptoms:

  • cat .git/HEAD shows ref: refs/heads/main but git branch does not list main.

Fix options:

  • If a remote branch exists:
    • git fetch origin
    • git symbolic-ref HEAD refs/heads/main
    • git branch -f main origin/main
    • git reset --hard origin/main
  • If you know the target commit (via CI logs, notes, or reflogs):
    • git update-ref refs/heads/main <commit>
    • git symbolic-ref HEAD refs/heads/main
  1. Branch ref exists but points to a missing commit

Symptoms:

  • git show-ref --heads shows refs/heads/main <hash>, but git show <hash> fails.

Fix:

  • Fetch missing objects: git fetch --all --prune --tags
  • If this branch tracks a remote: git reset --hard origin/main
  • If history was rewritten on the remote, align with the new tip:
    • git fetch origin
    • git branch -f main origin/main
    • git reset --hard origin/main
  1. Recover from reflogs (local recovery)
  • Show branch reflog (may work even if HEAD is broken):
    • git reflog show refs/heads/main
  • Pick a known-good commit from the reflog, then:
    • git update-ref refs/heads/main <good-commit>
    • git symbolic-ref HEAD refs/heads/main
    • git reset --hard <good-commit>
  1. Detect and address corruption
  • Verify object database: git fsck --full
    • Missing blobs/commits: fetch from remote or restore from backup.
    • Dangling objects are usually benign; missing required objects are not.
  • Try cleanup: git gc --prune=now --aggressive (can be slow; see Performance notes).
  • If corruption persists and you have a remote, reclone:
    • git clone --mirror <url>
    • Validate with git fsck, then replace the broken repo if needed.
  1. Special environments
  • Worktrees: run git worktree list to find the main worktree. Refs live in the primary repository’s .git directory; fix refs there if needed.
  • Submodules: cd into the submodule directory and apply the same diagnostics. Each submodule has its own .git data (sometimes as a gitdir link).
  1. Verify and prevent recurrence
  • Verify:
    • git rev-parse HEAD
    • git log -1 --oneline
    • git status
  • Prevention:
    • Avoid manual edits to .git files; use git symbolic-ref and git update-ref.
    • Keep remotes up to date: git fetch --all regularly.
    • Avoid long-lived detached HEADs for active development.

Pitfalls

  • Resetting the wrong branch: Always confirm the branch name you set in HEAD.
  • Hard resets discard uncommitted changes: stash or commit first.
  • Forcing HEAD to a non-tracking branch can break tooling that expects origin/<branch>.
  • Manually editing .git/HEAD or packed-refs risks typos and further breakage; prefer plumbing commands.
  • In CI, shallow clones may lack required objects; use fetch-depth: 0 or an explicit fetch after checkout.

Performance notes

  • git fsck --full and git gc --aggressive are CPU and I/O heavy on large repos; run them sparingly and outside hot paths (e.g., not during every CI job).
  • When a remote has the full history, fetching the missing refs and resetting is usually faster than running aggressive garbage collection.
  • Recloning can be the fastest fix for large, corrupted working copies when network bandwidth is good.
  • Use sparse-checkout and partial clones to reduce object transfer in CI; these still maintain a valid HEAD.

Tiny FAQ

  • Why did this start after a rebase or filter?
    Your local branch ref may point to a commit that was rewritten and garbage-collected. Fetch and reset to the updated remote tip.

  • Can I just edit .git/HEAD?
    Yes, but it’s safer to run: git symbolic-ref HEAD refs/heads/<branch>.

  • How do I know my default branch name?
    Try: git symbolic-ref --short HEAD (when healthy), or check remote: git ls-remote --symref origin HEAD.

  • Does recloning lose local work?
    Unpushed commits will be lost unless you export them first (git bundle create, or git format-patch) and import into the new clone.

Series: Git

DevOps