gastownhall/beads · error
failed to write post-merge hook: %w
Error message
failed to write post-merge hook: %w
What it means
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.
Source
Thrown at cmd/bd/init_git_hooks.go:197
postMergePath := filepath.Join(hooksDir, "post-merge")
postMergeContent := buildPostMergeHook(chainHooks, existingHooks)
// Normalize line endings to LF — on Windows/NTFS, Go string literals
// are fine but concatenated content from other sources may have CRLF.
// Git hooks with CRLF fail: /usr/bin/env: 'sh\r': No such file or directory
preCommitContent = strings.ReplaceAll(preCommitContent, "\r\n", "\n")
postMergeContent = strings.ReplaceAll(postMergeContent, "\r\n", "\n")
// Write pre-commit hook (executable scripts need 0700)
// #nosec G306 - git hooks must be executable
if err := os.WriteFile(preCommitPath, []byte(preCommitContent), 0700); err != nil {
return fmt.Errorf("failed to write pre-commit hook: %w", err)
}
// Write post-merge hook (executable scripts need 0700)
// #nosec G306 - git hooks must be executable
if err := os.WriteFile(postMergePath, []byte(postMergeContent), 0700); err != nil {
return fmt.Errorf("failed to write post-merge hook: %w", err)
}
if chainHooks {
fmt.Printf("%s Chained bd hooks with existing hooks\n", ui.RenderPass("✓"))
}
return nil
}
// buildPreCommitHook generates the pre-commit hook content using section markers (GH#1380).
// If chainHooks is true, chained hooks (.old) are called before the beads section.
func buildPreCommitHook(chainHooks bool, existingHooks []hookInfo) string {
section := generateHookSection("pre-commit")
if chainHooks {
var existingPreCommit string
for _, hook := range existingHooks {
if hook.name == "pre-commit" && hook.exists && !hook.isBdHook {View on GitHub (pinned to 71377f2769)
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.
Example fix
// before $ ls -la .git/hooks pre-commit (new, ok) post-merge: permission denied // after $ rm -f .git/hooks/post-merge $ bd init
Defensive patterns
Strategy: validation
Validate before calling
pm := filepath.Join(hooksDir, "post-merge")
if fi, err := os.Stat(pm); err == nil && fi.Mode().Perm()&0200 == 0 {
return fmt.Errorf("%s exists and is not overwritable", pm)
} Try / catch
if err := installGitHooks(hooksDir); err != nil {
if strings.Contains(err.Error(), "failed to write post-merge hook") {
// pre-commit may already be installed; fix post-merge and re-run init
}
return err
} Prevention
- 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.
When it happens
Trigger: os.WriteFile(postMergePath, ...) errors — same causes as pre-commit: permission denied, read-only filesystem, disk full, or an unwritable existing post-merge file.
Common situations: Existing root-owned or non-writable post-merge hook in shared repos, CI environments mounting .git read-only, or transient IO errors mid-install.
Related errors
- failed to create hooks directory: %w
- failed to write pre-commit hook: %w
- not a git repository
- failed to install hooks: %w
- building hook migration plan: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/82016eab043811a6.
Report an issue: GitHub.