Skip to content
Back to skills

Worktree Index Corrupt Async Post Commit Hook

ASecurity

Fix `fatal: unable to read <sha>` or `invalid sha1 pointer in cache-tree of .git/worktrees/<name>/index` errors that surface when running git operations (`git status`, `git rebase`, `git pull`) in worktree B shortly after committing in sibling worktree A — when the project has an async post-commit hook (a hook that runs `&`, `nohup`, or "running ... in background"). Use when: (1) error message points at worktree B's index but you weren't editing B, (2) you just merged/squashed/committed in wo...

  • 3 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 6, 2026
ai-agentspythongobashgitdatabase

Works with

  • claude code
  • cli

Security analysis

A100/100

Scanned September 6, 2026

npx -y skills add wan-huiyan/agent-traffic-control --skill worktree-index-corrupt-async-post-commit-hook --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Worktree Index Corrupt Async Post Commit Hook?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Worktree Index Corrupt Async Post Commit Hook
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/wan-huiyan-worktree-index-corrupt-async-post-commit-hook/badge)](https://www.skillsdirectory.com/skills/wan-huiyan-worktree-index-corrupt-async-post-commit-hook)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: worktree-index-corrupt-async-post-commit-hook
description: |
  Fix `fatal: unable to read <sha>` or `invalid sha1 pointer in cache-tree of
  .git/worktrees/<name>/index` errors that surface when running git operations
  (`git status`, `git rebase`, `git pull`) in worktree B shortly after committing
  in sibling worktree A — when the project has an async post-commit hook (a hook
  that runs `&`, `nohup`, or "running ... in background"). Use when:
  (1) error message points at worktree B's index but you weren't editing B,
  (2) you just merged/squashed/committed in worktree A and immediately switched,
  (3) the project has a `[post-commit] ... running ... in background` log line
  on commit, (4) `git fsck` reports `missing blob` errors across MULTIPLE worktree
  indexes simultaneously. NOT for: stale lock files (.git/index.lock — separate
  pattern), branch-checked-out-elsewhere errors (use `gh-pr-merge-worktree-checkout-trap`),
  or single-worktree corruption (different cause).
author: Claude Code
version: 1.0.0
date: 2026-05-06
disable-model-invocation: true
---

# Worktree index corruption from async post-commit hooks

## Problem

You commit in worktree A. Immediately afterwards (within seconds), you run
`git status` / `git rebase origin/main` / `git fetch && git rebase` in
sibling worktree B and get:

```
fatal: unable to read cd2a732cd2d5ad030414b953e21b9e403ab88699
```

or

```
error: cd2a732c...: invalid sha1 pointer in cache-tree of
       .../.git/worktrees/<name>/index
error: missing blob b007cf60...
error: missing blob d4077a21...
```

The error blames worktree B, but worktree B was clean and untouched.
`git status` itself fails. `git rebase` aborts before doing anything.

## Why this happens

Linked git worktrees share the underlying object database (`.git/objects/`)
but have **per-worktree index files** at `.git/worktrees/<name>/index`. Each
index's cache-tree caches `(path → tree-sha)` mappings to make `git status`
fast.

When the project has an async post-commit hook — common patterns:

```bash
# hooks/post-commit
[post-commit] Python files changed — running doc update in background...
nohup python3 docs/generate_website.py > /dev/null 2>&1 &
```

— or any hook that writes new objects/refs/files asynchronously after
returning — the sequence

1. commit in worktree A → post-commit hook starts writing to `.git/objects/`
2. **before** the async write finishes, you switch to worktree B
3. worktree B reads its cached cache-tree, which references object SHAs
   that A's hook is mid-creating

means worktree B's cache-tree points at SHAs that are either not-yet-flushed
or were transiently visible and then replaced. The cache-tree is now
inconsistent with the actual object DB. Every subsequent git command in B
that consults the index fails before it even reads HEAD.

The `fsck` confirms it cross-cuts ALL siblings:

```
error: <sha>: invalid sha1 pointer in cache-tree of
       .../.git/worktrees/<other-worktree-A>/index
error: <different-sha>: invalid sha1 pointer in cache-tree of
       .../.git/worktrees/<other-worktree-B>/index
error: <yet-another-sha>: invalid sha1 pointer in cache-tree of
       .../.git/worktrees/<other-worktree-C>/index
```

Multiple worktrees' indexes are corrupted simultaneously — that's the
fingerprint that distinguishes this from a normal index lock or a
branch-elsewhere error.

## Fix (one-liner per affected worktree)

```sh
# In the broken worktree:
rm /path/to/main/repo/.git/worktrees/<this-worktree>/index
git reset --mixed HEAD
```

That's it. The index is a derived cache; deleting it forces git to rebuild
from the on-disk files + HEAD. No data loss because the working tree's
actual file content is untouched. Any staged changes are lost — but if
you got here, you weren't mid-edit anyway.

For `<this-worktree>`: it's the basename of the worktree directory. Or
look at the `.git` file in your worktree dir:

```sh
cat .git
# gitdir: /path/to/main/repo/.git/worktrees/<NAME>
```

## Prevention

Before running git ops in a sibling worktree right after a commit elsewhere,
either:

1. **Wait for the hook to finish.** If the hook line says "running in
   background" you can usually wait 5-10 seconds and the race vanishes.
2. **Pre-emptively rebuild the index** in any worktree you're about to
   operate on, immediately after commits land in siblings:

   ```sh
   rm .git/worktrees/<name>/index 2>/dev/null
   git reset --mixed HEAD
   ```

   Cheap (~50ms) and idempotent — safe to run unconditionally as a defensive
   step in scripts that orchestrate multi-worktree merges.

3. **Make the hook synchronous** if the project allows. `wait` at the end
   of the hook, or just drop the `&`/`nohup`. Slower commits but no race.

## Verification

After the fix:

```sh
git status              # should report clean / branch state without error
git fsck --no-dangling  # should report no errors on this worktree's index
```

If `fsck` still complains about other worktrees' indexes, run the same fix
in each of them. The corruption can hit several worktrees simultaneously
because they all share the object DB.

## Example (a client analytics repo, S130 merge orchestration)

Context: 5 parallel feature-branch worktrees, sequentially squash-merging
PRs via `gh pr merge`. The project's post-commit hook prints
`[post-commit] Python files changed — running doc update in background...`
and forks a generator. Right after merging PR #244 in worktree A and
running `git fetch && git rebase origin/main` in worktree B (record-drawer-ux):

```
$ git rebase origin/main
error: cannot rebase: You have unstaged changes.
$ git status
fatal: unable to read cd2a732cd2d5ad030414b953e21b9e403ab88699
$ git fsck --no-dangling
error: 7c813df3...: invalid sha1 pointer in cache-tree of
       .../.git/worktrees/new-dashboard-bugs/index
error: 6c1ddfb5...: invalid sha1 pointer in cache-tree of
       .../.git/worktrees/record-drawer-ux/index
error: 6c1ddfb5...: invalid sha1 pointer in cache-tree of
       .../.git/worktrees/clarify/index
error: 7c813df3...: invalid sha1 pointer in cache-tree of
       .../.git/worktrees/110b/index
missing blob b007cf60...
missing blob d4077a21...
[...]
```

Four worktrees corrupted simultaneously — the cross-cut signature. Fix:

```sh
rm /Users/.../the-project-repo/.git/worktrees/record-drawer-ux/index
git reset --mixed HEAD
# clean
git rebase origin/main   # works
```

Same one-liner applied to each subsequent worktree before its rebase.
Pre-emptively running it on the *next* worktree before rebasing it
eliminated the race for the rest of the merge sequence.

## Notes

- This is distinct from `.git/index.lock` errors (different fix: just
  remove the lock file) and from `error: cannot lock ref` (concurrent
  ref-update race, different fix).
- The error message is misleading. It names the worktree where the command
  was run, not the worktree whose hook caused the race. `git fsck` is the
  diagnostic that reveals the cross-cut.
- `git read-tree HEAD` is an alternative to `git reset --mixed HEAD` for
  rebuilding the index, slightly more surgical (doesn't touch HEAD's
  staging state) but `--mixed` is more familiar.
- If the hook also writes to shared paths inside `.git/info/` or
  `.git/refs/`, even more bizarre symptoms can surface. The same root-cause
  applies — the fix is to ensure the hook's writes are synchronous OR to
  rebuild the affected index before next use.
- Project hooks that match this pattern usually log `[post-commit] ...
  running ... in background` or `[post-commit] ... &` to stdout/stderr.
  If you see such a line on commit, treat the whole repo as
  race-vulnerable across worktrees.

## See also

- `git-add-u-after-async-post-commit-hook` — sister skill, **same root
  cause family** (async post-commit hook side effects), different
  symptom: `git add -u && git commit --amend` accidentally rolls
  thousands of unrelated tracked-file deletions into the amended
  commit, then force-push propagates the catastrophe to the remote PR.
  This worktree-index skill covers the cross-worktree blob-GC race;
  the `git-add-u-after-...` skill covers the same-worktree index
  staging trap.
- `git-rebase-stalls-async-post-commit-hook` — sister skill, **same
  root cause family**, different symptom: multi-commit `git rebase
  origin/main` stalls mid-replay with `hint: ... It has been
  rescheduled` output, leaving stuck `rebase-merge` directories that
  `git rebase --abort` does NOT clear. Each rebase-applied commit
  fires the hook; the hook's spawned background subprocess races
  git's atomic apply machinery. Fix: `git -c core.hooksPath=/dev/null
  rebase origin/main`. Distinct symptom from the cross-worktree blob
  GC race covered here.
- `gh-pr-merge-worktree-checkout-trap` — *different* problem: gh refuses
  to delete a remote branch that's checked out in a sibling worktree.
- `using-git-worktrees` / `git-worktree` — general worktree workflow.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.