{"record":{"id":"82016eab043811a6","repo":"gastownhall/beads","slug":"failed-to-write-post-merge-hook-w","errorCode":null,"errorMessage":"failed to write post-merge hook: %w","messagePattern":"failed to write post-merge hook: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/init_git_hooks.go","lineNumber":197,"sourceCode":"\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 {\n\tsection := generateHookSection(\"pre-commit\")\n\n\tif chainHooks {\n\t\tvar existingPreCommit string\n\t\tfor _, hook := range existingHooks {\n\t\t\tif hook.name == \"pre-commit\" && hook.exists && !hook.isBdHook {","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/init_git_hooks.go#L179-L215","documentation":"The sibling of the pre-commit write: installGitHooks writes the post-merge hook with os.WriteFile(postMergePath, ..., 0700) and wraps any failure with this message. The pre-commit hook may already have been written, leaving a partially installed hook set.","triggerScenarios":"os.WriteFile(postMergePath, ...) errors — same causes as pre-commit: permission denied, read-only filesystem, disk full, or an unwritable existing post-merge file.","commonSituations":"Existing root-owned or non-writable post-merge hook in shared repos, CI environments mounting .git read-only, or transient IO errors mid-install.","solutions":["Inspect and remove/fix the conflicting `.git/hooks/post-merge` file, then re-run `bd init`.","Check .git/hooks directory permissions and free disk space.","Re-run `bd init` to install both hooks consistently (pre-commit may need rewriting too).","Confirm both hooks exist and are executable before committing."],"exampleFix":"// before\n$ ls -la .git/hooks\npre-commit (new, ok)  post-merge: permission denied\n// after\n$ rm -f .git/hooks/post-merge\n$ bd init","handlingStrategy":"validation","validationCode":"pm := filepath.Join(hooksDir, \"post-merge\")\nif fi, err := os.Stat(pm); err == nil && fi.Mode().Perm()&0200 == 0 {\n    return fmt.Errorf(\"%s exists and is not overwritable\", pm)\n}","typeGuard":null,"tryCatchPattern":"if err := installGitHooks(hooksDir); err != nil {\n    if strings.Contains(err.Error(), \"failed to write post-merge hook\") {\n        // pre-commit may already be installed; fix post-merge and re-run init\n    }\n    return err\n}","preventionTips":["Audit .git/hooks for conflicting post-merge scripts before installing bd hooks.","Ensure the repo filesystem is writable and not full.","Re-run bd init after any partial hook-install failure so both hooks are consistent.","Verify both pre-commit and post-merge exist and are executable."],"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"}