{"record":{"id":"36ddc16e9f97d24d","repo":"gastownhall/beads","slug":"failed-to-write-s-w","errorCode":null,"errorMessage":"failed to write %s: %w","messagePattern":"failed to write (.+?): %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/hooks.go","lineNumber":975,"sourceCode":"\t\t\t\t\tif _, statErr := os.Lstat(backupPath); os.IsNotExist(statErr) {\n\t\t\t\t\t\t// #nosec G306 -- keep executable so a rename restores a working hook\n\t\t\t\t\t\tif backupErr := os.WriteFile(backupPath, existing, 0755); backupErr != nil {\n\t\t\t\t\t\t\treturn fmt.Errorf(\"failed to back up %s before injecting bd section: %w\", hookName, backupErr)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Inject section, preserving existing content\n\t\t\t\t\tnewContent = injectHookSection(existingStr, section)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Normalize line endings to LF\n\t\tnewContent = strings.ReplaceAll(newContent, \"\\r\\n\", \"\\n\")\n\n\t\t// Write hook file\n\t\t// #nosec G306 -- git hooks must be executable for Git to run them\n\t\tif err := os.WriteFile(hookPath, []byte(newContent), 0755); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to write %s: %w\", hookName, err)\n\t\t}\n\t}\n\n\t// Configure git to use the hooks directory\n\tif beadsHooks {\n\t\tif err := configureBeadsHooksPath(); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to configure git hooks path: %w\", err)\n\t\t}\n\t} else if shared {\n\t\tif err := configureSharedHooksPath(); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to configure git hooks path: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// preservePreexistingHooks copies non-beads hooks from the currently effective","sourceCodeStart":957,"sourceCodeEnd":993,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/hooks.go#L957-L993","documentation":"After composing the new hook content (shebang plus bd section, merged with preserved user content and normalized to LF), bd writes the hook file with os.WriteFile(..., 0755). A failed write aborts installation with this wrapped error. Since bd installs hooks atomically after a pre-flight guard, a failure here is almost always environmental, not logical.","triggerScenarios":"os.WriteFile(hookPath, ...) fails during installHooksWithOptions: directory not writable, disk full, read-only mount, or the target was replaced by something unwritable between the guard check and the write (TOCTOU).","commonSituations":"Root-owned .git/hooks; CI containers with read-only source mounts; full disk; antivirus/backup tools locking files on network filesystems.","solutions":["Fix permissions on the hooks directory: `sudo chown -R $(whoami) <hooksDir>`","Free disk space or remount read-only volumes read-write","Check for an external process holding/locking the file and retry","Re-run `bd hooks install` after remediation"],"exampleFix":"// before\n$ bd hooks install\nfailed to write pre-commit: permission denied\n// after\n$ sudo chown $(whoami) .git/hooks && bd hooks install","handlingStrategy":"validation","validationCode":"d=\"$(git rev-parse --git-path hooks 2>/dev/null)\"; [ -n \"$d\" ] && [ -w \"$d\" ] && [ \"$(stat -f %z \"$d\" 2>/dev/null || echo dir)\" ] || echo \"check $d writability\"","typeGuard":null,"tryCatchPattern":"if err := installHooks(...); err != nil { var pe *fs.PathError; if errors.As(err, &pe) && errors.Is(pe.Err, syscall.ENOSPC) { /* free disk, retry */ }; if strings.Contains(err.Error(), \"failed to write\") { /* fix perms/mount, retry */ } }","preventionTips":["Own the checkout with the same user bd runs as","Avoid read-only source mounts when installing hooks","Pre-flight writability of the hooks dir in automation"],"tags":["filesystem","permissions","hooks"],"backgroundTag":"file-write-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}