{"record":{"id":"57d28b69c1cfdd4e","repo":"gastownhall/beads","slug":"failed-to-stat-s-w","errorCode":null,"errorMessage":"failed to stat %s: %w","messagePattern":"failed to stat (.+?): %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/hooks.go","lineNumber":818,"sourceCode":"\t\treturn nil\n\t},\n}\n\n// guardHookWritePath refuses hook writes that would silently modify files\n// bd does not own (bd-5vdt8):\n//   - a symlinked hook path: os.WriteFile follows the link (O_TRUNC) and\n//     rewrites the target inode — e.g. a repo-tracked script the hook\n//     points at, dirtying every clone that shares it\n//   - a hook file tracked by git: writing it dirties the working tree.\n//     allowTracked exempts shared installs (.beads-hooks/ is deliberately\n//     committed).\nfunc guardHookWritePath(hookPath string, allowTracked bool) error {\n\tfi, err := os.Lstat(hookPath)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil // creating a new file — nothing to clobber\n\t\t}\n\t\treturn fmt.Errorf(\"failed to stat %s: %w\", hookPath, err)\n\t}\n\tif fi.Mode()&os.ModeSymlink != 0 {\n\t\ttarget := \"unresolvable target\"\n\t\tif resolved, rerr := filepath.EvalSymlinks(hookPath); rerr == nil {\n\t\t\ttarget = resolved\n\t\t} else if link, lerr := os.Readlink(hookPath); lerr == nil {\n\t\t\ttarget = link\n\t\t}\n\t\treturn fmt.Errorf(\"%s is a symlink to %s; writing would rewrite the link target, not the hook\\nRemove the symlink (or leave that hook to its owner) and re-run\", hookPath, target)\n\t}\n\tif allowTracked {\n\t\treturn nil\n\t}\n\t// A tracked file is refused only when bd does NOT own it: writing into a\n\t// foreign tracked file dirties every clone that shares it (the wy-81fnur\n\t// incident). A bd-owned hook the user chose to commit (e.g. a team-shared\n\t// .beads/hooks/) is bd's to maintain — same policy as shared installs.\n\tif isGitTrackedFile(hookPath) && !isBdOwnedHookFile(hookPath) {","sourceCodeStart":800,"sourceCodeEnd":836,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/hooks.go#L800-L836","documentation":"Wraps an os.Lstat failure in guardHookWritePath when the hook path exists but cannot be stat'd for a reason other than not-exist. guardHookWritePath pre-checks the target before bd writes a hook so it never clobbers symlinks or foreign tracked files.","triggerScenarios":"installHooksWithOptions or applyHookMigrationExecution calls guardHookWritePath(hookPath, allowTracked); os.Lstat fails with an error where os.IsNotExist(err) is false (e.g. permission denied on a parent directory).","commonSituations":"Parent .git/hooks directory lacks execute/read permission, sandboxed environment blocking stat, filesystem error, or a path component that is inaccessible.","solutions":["Fix permissions on the .git/hooks directory (chmod u+rx .git/hooks) and retry.","Check the wrapped cause (%w) for the exact errno and address it (ownership, ACL, mount).","Run outside restricted sandboxes or grant the process access to the repo's .git directory."],"exampleFix":"// before\n$ bd hooks install\n// error: failed to stat .git/hooks/pre-commit: permission denied\n// after\n$ chmod u+rx .git/hooks\n$ bd hooks install","handlingStrategy":"validation","validationCode":"if fi, err := os.Lstat(hookPath); err != nil && !os.IsNotExist(err) {\n\treturn fmt.Errorf(\"cannot access hook path %s: %w\", hookPath, err)\n}","typeGuard":null,"tryCatchPattern":"if err := guardHookWritePath(hookPath, allowTracked); err != nil {\n\tif strings.Contains(err.Error(), \"failed to stat\") {\n\t\t// fix permissions or abort install with guidance\n\t}\n}","preventionTips":["Ensure .git and .git/hooks are accessible (u+rx) to the user running bd.","Run hooks installation outside restricted sandboxes/containers.","Check the wrapped errno in the cause to target the fix."],"tags":["git-hooks","filesystem","permissions","wrapped-error"],"backgroundTag":"stat-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}