gastownhall/beads · error
failed to check git index lock %s: %w
Error message
failed to check git index lock %s: %w
What it means
Before staging, gitAddFile resolves the index lock path and calls os.Stat on it. If Stat fails with an error other than NotExist (e.g. permission denied on the .git directory), the lock cannot be verified, so the function fails with this wrapped error rather than guessing. This protects auto-export from staging into an unknown index state.
Source
Thrown at cmd/bd/export_auto.go:851
// 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))
cmd.Dir = filepath.Dir(path)
cmd.Env = envView on GitHub (pinned to 71377f2769)
Solutions
- Fix permissions on the .git directory so the user running bd can stat files there (chmod/chown as appropriate)
- Verify you are operating inside the correct repo/worktree and .git is accessible
- If in a worktree/hook context, check that scrubGitHookEnv did not strip needed GIT_DIR context; run bd from the repo root
Example fix
// before // failed to check git index lock /repo/.git/index.lock: permission denied // after sudo chown -R $(whoami) /repo/.git bd sync
Defensive patterns
Strategy: validation
Validate before calling
// Ensure .git is accessible before running bd
gitDir := ".git"
if _, err := os.Stat(gitDir); err != nil {
log.Fatal("cannot access .git: ", err)
}
if _, err := os.Stat(filepath.Join(gitDir, "index.lock")); err != nil && !os.IsNotExist(err) {
log.Fatal("cannot stat index lock (permissions?): ", err)
} Try / catch
if err := sync(); err != nil && strings.Contains(err.Error(), "failed to check git index lock") {
// Fix filesystem access, then retry
fixGitPermissions()
sync()
} Prevention
- Run bd as the same user that owns the repo/.git
- Avoid running bd against repos on flaky network filesystems
- Verify permissions after cloning with sudo or changing users
- Test `git status` works in your environment before automated bd runs
When it happens
Trigger: gitIndexLockPath succeeded and returned a lockPath, but os.Stat(lockPath) returned a non-ENOENT error — e.g. the .git directory or lock file is not readable/stat-able by the current user, or a filesystem error occurred.
Common situations: Running bd as a different user than the repo owner; restrictive permissions on .git; network filesystems returning transient stat errors; sandboxed CI environments blocking .git access.
Related errors
- dolt path is not executable
- failed to create backup directory: %w
- failed to create temp file: %w
- create beads directory: %w
- write metadata.json: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/da7839054a1be63f.
Report an issue: GitHub.