{"record":{"id":"da7839054a1be63f","repo":"gastownhall/beads","slug":"failed-to-check-git-index-lock-s-w","errorCode":null,"errorMessage":"failed to check git index lock %s: %w","messagePattern":"failed to check git index lock (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/export_auto.go","lineNumber":851,"sourceCode":"// gitAddFile stages a file in the enclosing git repo. When called from\n// inside a git hook, it scrubs inherited GIT_* env vars (so git\n// rediscovers the repo from cwd rather than treating cmd.Dir as the\n// worktree root) and skips staging when the target is outside the hook's\n// worktree (the .beads/redirect case, where staging would pollute the\n// main repo's index). See GH#3311, scrubGitHookEnv, hookWorkTreeRoot.\nfunc gitAddFile(path string) error {\n\tif wt := hookWorkTreeRoot(); wt != \"\" && !pathInsideDir(path, wt) {\n\t\t// Running inside a hook AND target is outside the hook's worktree.\n\t\t// Staging here would pollute a different repo's index; skip.\n\t\treturn nil\n\t}\n\n\tenv := scrubGitHookEnv(os.Environ())\n\tif lockPath, err := gitIndexLockPath(path, env); err == nil && lockPath != \"\" {\n\t\tif _, statErr := os.Stat(lockPath); statErr == nil {\n\t\t\treturn fmt.Errorf(\"git index is locked at %s; skipping auto-stage\", lockPath)\n\t\t} else if !os.IsNotExist(statErr) {\n\t\t\treturn fmt.Errorf(\"failed to check git index lock %s: %w\", lockPath, statErr)\n\t\t}\n\t} else if err != nil {\n\t\tdebug.Logf(\"auto-export: git add lock preflight skipped: %v\\n\", err)\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), gitAddTimeout)\n\tdefer cancel()\n\t// Pass the basename only, defensively: cmd.Dir is the parent of path, so\n\t// a full path argument would double-root (cd .beads && git add\n\t// .beads/issues.jsonl → pathspec looks under .beads/.beads/) if a caller\n\t// ever passed a relative path here. Both current callers pass absolute\n\t// paths, so this guards against a regression rather than fixing a live\n\t// failure. See GH#4351.\n\t// Keep cmd.Dir = parent so GH#3311 hook worktree staging still resolves\n\t// the index path under the repo root (not bare \"issues.jsonl\" at root).\n\tcmd := exec.CommandContext(ctx, \"git\", \"add\", \"--\", filepath.Base(path))\n\tcmd.Dir = filepath.Dir(path)\n\tcmd.Env = env","sourceCodeStart":833,"sourceCodeEnd":869,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/export_auto.go#L833-L869","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n// failed to check git index lock /repo/.git/index.lock: permission denied\n// after\nsudo chown -R $(whoami) /repo/.git\nbd sync","handlingStrategy":"validation","validationCode":"// Ensure .git is accessible before running bd\ngitDir := \".git\"\nif _, err := os.Stat(gitDir); err != nil {\n    log.Fatal(\"cannot access .git: \", err)\n}\nif _, err := os.Stat(filepath.Join(gitDir, \"index.lock\")); err != nil && !os.IsNotExist(err) {\n    log.Fatal(\"cannot stat index lock (permissions?): \", err)\n}","typeGuard":null,"tryCatchPattern":"if err := sync(); err != nil && strings.Contains(err.Error(), \"failed to check git index lock\") {\n    // Fix filesystem access, then retry\n    fixGitPermissions()\n    sync()\n}","preventionTips":["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"],"tags":["git","filesystem","permissions","auto-export"],"backgroundTag":"git-index-lock-check-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}