gastownhall/beads · error
failed to write pre-commit hook: %w
Error message
failed to write pre-commit hook: %w
What it means
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.
Source
Thrown at cmd/bd/init_git_hooks.go:191
// pre-commit hook
preCommitPath := filepath.Join(hooksDir, "pre-commit")
preCommitContent := buildPreCommitHook(chainHooks, existingHooks)
// post-merge hook
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 {View on GitHub (pinned to 71377f2769)
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.
Example fix
// before $ ls -la .git/hooks/pre-commit -rwxr-xr-x 1 root root ... (root-owned) // after $ sudo rm .git/hooks/pre-commit $ bd init
Defensive patterns
Strategy: validation
Validate before calling
pc := filepath.Join(hooksDir, "pre-commit")
if fi, err := os.Stat(pc); err == nil && (!fi.Mode().Perm()&0200 != 0 || isForeignOwner(fi)) {
return fmt.Errorf("%s exists and is not overwritable", pc)
} Try / catch
if err := installGitHooks(hooksDir); err != nil {
if strings.Contains(err.Error(), "failed to write pre-commit hook") {
var pe *fs.PathError
if errors.As(err, &pe) { /* handle pe.Err: EACCES -> fix perms; ENOSPC -> free disk */ }
}
return err
} Prevention
- 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).
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to create hooks directory: %w
- failed to write post-merge 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/d0230f158515e0d4.
Report an issue: GitHub.