gastownhall/beads · warning
git index is locked at %s; skipping auto-stage
Error message
git index is locked at %s; skipping auto-stage
What it means
gitAddFile runs a preflight that resolves the git index lock path (via `git rev-parse --git-dir`) and checks whether index.lock exists. If the lock file is present, it skips staging and returns this error so auto-export does not fight a concurrent git operation. It is a deliberate skip, not a crash of the export itself.
Source
Thrown at cmd/bd/export_auto.go:849
}
// gitAddFile stages a file in the enclosing git repo. When called from
// inside a git hook, it scrubs inherited GIT_* env vars (so git
// rediscovers the repo from cwd rather than treating cmd.Dir as the
// worktree root) and skips staging when the target is outside the hook's
// worktree (the .beads/redirect case, where staging would pollute the
// main repo's index). See GH#3311, scrubGitHookEnv, hookWorkTreeRoot.
func gitAddFile(path string) error {
if wt := hookWorkTreeRoot(); wt != "" && !pathInsideDir(path, wt) {
// Running inside a hook AND target is outside the hook's worktree.
// Staging here would pollute a different repo's index; skip.
return nil
}
env := scrubGitHookEnv(os.Environ())
if lockPath, err := gitIndexLockPath(path, env); err == nil && lockPath != "" {
if _, statErr := os.Stat(lockPath); statErr == nil {
return fmt.Errorf("git index is locked at %s; skipping auto-stage", lockPath)
} else if !os.IsNotExist(statErr) {
return fmt.Errorf("failed to check git index lock %s: %w", lockPath, statErr)
}
} else if err != nil {
debug.Logf("auto-export: git add lock preflight skipped: %v\n", err)
}
ctx, cancel := context.WithTimeout(context.Background(), gitAddTimeout)
defer cancel()
// Pass the basename only, defensively: cmd.Dir is the parent of path, so
// a full path argument would double-root (cd .beads && git add
// .beads/issues.jsonl → pathspec looks under .beads/.beads/) if a caller
// ever passed a relative path here. Both current callers pass absolute
// paths, so this guards against a regression rather than fixing a live
// failure. See GH#4351.
// Keep cmd.Dir = parent so GH#3311 hook worktree staging still resolves
// the index path under the repo root (not bare "issues.jsonl" at root).
cmd := exec.CommandContext(ctx, "git", "add", "--", filepath.Base(path))View on GitHub (pinned to 71377f2769)
Solutions
- Wait for the concurrent git operation to finish and let the next auto-export stage the file
- If no git process is running, remove the stale lock: `rm -f .git/index.lock` (or the path shown in the error), then retry
- Run `bd sync` / explicit export to force staging once the lock clears
Example fix
// before // git index is locked at /repo/.git/index.lock; skipping auto-stage // after rm -f /repo/.git/index.lock # only if no git process is actually running bd sync
Defensive patterns
Strategy: retry
Validate before calling
// Preflight: check for a live index.lock before exporting
if _, err := os.Stat(filepath.Join(gitDir, "index.lock")); err == nil {
fmt.Println("git index locked; wait for the git operation to finish")
} Try / catch
if err := autoExport(); err != nil && strings.Contains(err.Error(), "index is locked") {
time.Sleep(2 * time.Second)
err = autoExport() // retry after the concurrent git op likely finished
} Prevention
- Don't run concurrent git mutations (GUI commits, rebases) while bd hooks fire
- Clean up stale index.lock files after crashed git processes
- Serialize bd auto-export with other git automation in CI
- Check `git status` health before long sync sessions
When it happens
Trigger: gitAddFile (called from maybeAutoExport during auto-export) finds an existing index.lock in the resolved git dir at the moment it tries to stage the JSONL — typically while another git process (commit, rebase, another bd hook) holds the index.
Common situations: A git GUI, IDE, or parallel `git commit` is running when bd's post-hook fires; a stale index.lock left by a crashed git process; two bd instances auto-exporting concurrently.
Related errors
- auto-export: git add failed: %w
- failed to check git index lock %s: %w
- git add timed out after %s
- %w: %s
- failed to inspect target cleanliness: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/241525411c46a6c5.
Report an issue: GitHub.