{"record":{"id":"d0230f158515e0d4","repo":"gastownhall/beads","slug":"failed-to-write-pre-commit-hook-w","errorCode":null,"errorMessage":"failed to write pre-commit hook: %w","messagePattern":"failed to write pre-commit hook: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/init_git_hooks.go","lineNumber":191,"sourceCode":"\n\t// pre-commit hook\n\tpreCommitPath := filepath.Join(hooksDir, \"pre-commit\")\n\tpreCommitContent := buildPreCommitHook(chainHooks, existingHooks)\n\n\t// post-merge hook\n\tpostMergePath := filepath.Join(hooksDir, \"post-merge\")\n\tpostMergeContent := buildPostMergeHook(chainHooks, existingHooks)\n\n\t// Normalize line endings to LF — on Windows/NTFS, Go string literals\n\t// are fine but concatenated content from other sources may have CRLF.\n\t// Git hooks with CRLF fail: /usr/bin/env: 'sh\\r': No such file or directory\n\tpreCommitContent = strings.ReplaceAll(preCommitContent, \"\\r\\n\", \"\\n\")\n\tpostMergeContent = strings.ReplaceAll(postMergeContent, \"\\r\\n\", \"\\n\")\n\n\t// Write pre-commit hook (executable scripts need 0700)\n\t// #nosec G306 - git hooks must be executable\n\tif err := os.WriteFile(preCommitPath, []byte(preCommitContent), 0700); err != nil {\n\t\treturn fmt.Errorf(\"failed to write pre-commit hook: %w\", err)\n\t}\n\n\t// Write post-merge hook (executable scripts need 0700)\n\t// #nosec G306 - git hooks must be executable\n\tif err := os.WriteFile(postMergePath, []byte(postMergeContent), 0700); err != nil {\n\t\treturn fmt.Errorf(\"failed to write post-merge hook: %w\", err)\n\t}\n\n\tif chainHooks {\n\t\tfmt.Printf(\"%s Chained bd hooks with existing hooks\\n\", ui.RenderPass(\"✓\"))\n\t}\n\n\treturn nil\n}\n\n// buildPreCommitHook generates the pre-commit hook content using section markers (GH#1380).\n// If chainHooks is true, chained hooks (.old) are called before the beads section.\nfunc buildPreCommitHook(chainHooks bool, existingHooks []hookInfo) string {","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/init_git_hooks.go#L173-L209","documentation":"installGitHooks writes the pre-commit hook script to the hooks directory with os.WriteFile(..., 0700). A write failure is wrapped with this message. The hook is not installed, so bd's pre-commit automation will not run.","triggerScenarios":"os.WriteFile(preCommitPath, ...) errors — permission denied on .git/hooks, disk full, hooks dir deleted between MkdirAll and write, or an unwritable existing pre-commit file owned by another user.","commonSituations":"Shared repos where a root-owned pre-commit hook already exists, container/CI environments with read-only .git, or anti-virus/monitoring locking the hook file on Windows.","solutions":["Check permissions on .git/hooks and any existing pre-commit file; remove or chown conflicting files: `ls -la .git/hooks/pre-commit`.","Free disk space if the filesystem is full.","Re-run `bd init` after fixing the environment.","Verify the written hook: `.git/hooks/pre-commit` should exist and be executable."],"exampleFix":"// before\n$ ls -la .git/hooks/pre-commit\n-rwxr-xr-x 1 root root ... (root-owned)\n// after\n$ sudo rm .git/hooks/pre-commit\n$ bd init","handlingStrategy":"validation","validationCode":"pc := filepath.Join(hooksDir, \"pre-commit\")\nif fi, err := os.Stat(pc); err == nil && (!fi.Mode().Perm()&0200 != 0 || isForeignOwner(fi)) {\n    return fmt.Errorf(\"%s exists and is not overwritable\", pc)\n}","typeGuard":null,"tryCatchPattern":"if err := installGitHooks(hooksDir); err != nil {\n    if strings.Contains(err.Error(), \"failed to write pre-commit hook\") {\n        var pe *fs.PathError\n        if errors.As(err, &pe) { /* handle pe.Err: EACCES -> fix perms; ENOSPC -> free disk */ }\n    }\n    return err\n}","preventionTips":["Check for pre-existing root-owned hooks before installing.","Keep adequate disk space on the repo volume.","Don't run bd init from accounts that can't write .git/hooks.","After init, confirm the pre-commit hook is executable (mode 0700)."],"tags":["git","hooks","filesystem"],"backgroundTag":"file-write-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}