gastownhall/beads · error
failed to read %s: %w
Error message
failed to read %s: %w
What it means
Before merging its section, bd reads the existing hook file with os.ReadFile; if the read fails for any reason other than not-exist, installation aborts with this wrapped error. Missing files are handled normally (a fresh hook is created), so this indicates the path exists but is unreadable or is a special/dangling file.
Source
Thrown at cmd/bd/hooks.go:933
for _, hookName := range hookNames {
if err := guardHookWritePath(filepath.Join(hooksDir, hookName), shared); err != nil {
return fmt.Errorf("refusing to install %s hook: %w", hookName, err)
}
}
// Install each hook using section markers (GH#1380).
// Only the content between markers is managed by beads; user content
// outside the markers is preserved across reinstalls and upgrades.
for _, hookName := range hookNames {
hookPath := filepath.Join(hooksDir, hookName)
section := generateHookSection(hookName)
// Read existing hook file (if any)
// #nosec G304 -- hook path constrained to hooks directory
existing, readErr := os.ReadFile(hookPath)
if readErr != nil && !os.IsNotExist(readErr) {
return fmt.Errorf("failed to read %s: %w", hookName, readErr)
}
var newContent string
if os.IsNotExist(readErr) {
// No existing file — create with shebang + section
newContent = "#!/usr/bin/env sh\n" + section
} else {
existingStr := string(existing)
// Check if file already has section markers
if strings.Contains(existingStr, hookSectionBeginPrefix) {
// Update only the section between markers
newContent = injectHookSection(existingStr, section)
} else {
// Check if this is a legacy bd hook (shim or inline)
versionInfo, _ := getHookVersion(hookPath)
if versionInfo.IsBdHook {
// Legacy bd hook — replace entire file with section format
newContent = "#!/usr/bin/env sh\n" + sectionView on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped OS error (permission denied vs is-a-directory)
- Fix permissions: `chmod u+r <hooksDir>/<hook>` or re-own with chown
- If a directory occupies the hook name, remove it (`rm -rf .git/hooks/<name>`) and re-run
- Re-run `bd hooks install` after fixing
Example fix
// before $ ls -l .git/hooks/pre-commit # owned by root, mode 0600 // after $ sudo chown $(whoami) .git/hooks/pre-commit && bd hooks install
Defensive patterns
Strategy: validation
Validate before calling
p="$hooksDir/pre-commit"; if [ -d "$p" ]; then echo "$p is a directory, not a file"; fi; [ -r "$p" ] || echo "$p not readable"
Try / catch
if err := installHooks(...); err != nil { if strings.Contains(err.Error(), "failed to read") { /* fix perms/special file, then retry */ } } Prevention
- Keep hook paths as plain readable files
- Avoid running bd as a different user than the repo owner
- Check for stray directories named like hooks in the hooks dir
When it happens
Trigger: installHooksWithOptions reads hookPath and gets an error that is not os.IsNotExist — permission denied, path is a directory, I/O error on a dangling/special file.
Common situations: Hook file owned by root while bd runs as another user; a directory named `pre-commit` left in the hooks dir; corrupted filesystem; unusual ACLs.
Related errors
- failed to create hooks directory: %w
- failed to write %s: %w
- dolt path is not executable
- failed to create backup directory: %w
- failed to create temp file: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/664ad572959c7c94.
Report an issue: GitHub.