gastownhall/beads · error
failed to write .gitignore: %w
Error message
failed to write .gitignore: %w
What it means
When user content remains after stripping the beads section, the function rewrites .gitignore via os.WriteFile with mode 0644. Any write error (permission denied, read-only filesystem, disk full, path turned into a directory) is wrapped with %w and returned. This is the commit-step of the cleanup: the in-memory newContent was built but could not be persisted.
Source
Thrown at cmd/bd/init_stealth.go:221
}
out = append(out, lines[i])
}
if !changed {
return false, nil
}
newContent := strings.Join(out, "\n")
if strings.TrimSpace(newContent) == "" {
// beads was the only reason this .gitignore existed — remove it for true stealth.
if err := os.Remove(gitignorePath); err != nil {
return false, fmt.Errorf("failed to remove emptied .gitignore: %w", err)
}
return true, nil
}
// #nosec G306 - gitignore needs to be readable by git and collaborators
if err := os.WriteFile(gitignorePath, []byte(newContent), 0644); err != nil {
return false, fmt.Errorf("failed to write .gitignore: %w", err)
}
return true, nil
}
// isStealthRepo reports whether beads must keep its footprint out of tracked git files for the
// workspace at repoPath. It keys off the persisted no-git-ops flag — the same signal bd prime uses
// for the stealth session-close protocol (GH#593). bd init --stealth sets it, and a user may also
// set it directly; either way beads routes ignores into .git/info/exclude rather than a tracked
// .gitignore.
func isStealthRepo(repoPath string) bool {
beadsDir := doctor.ResolveBeadsDirForRepo(repoPath)
return config.GetStringFromDir(beadsDir, "no-git-ops") == "true"
}
// trackedGitignoreHasBeadsSection reports whether the tracked project-root .gitignore at repoPath
// still carries the bd-managed section header — i.e. a previous run leaked Dolt patterns into a
// git-visible file. Used by the stealth doctor check to flag the leak for --fix to clean up.
func trackedGitignoreHasBeadsSection(repoPath string) bool {View on GitHub (pinned to 71377f2769)
Solutions
- Fix ownership/permissions: sudo chown $(whoami) .gitignore && chmod 644 .gitignore, and ensure the repo directory is writable (chmod u+w).
- Free disk space / check quota if the error wraps ENOSPC (df -h).
- If the mount is read-only, remount or clone the repo into a writable path.
- Disable or pause file-sync/AV tools that lock .gitignore, then retry the fix.
- Manually edit .gitignore to remove the beads section, then re-run bd stealth fix to verify.
Example fix
// before (file owned by root) -rw-r--r-- 1 root root 512 .gitignore // after $ sudo chown $(whoami) .gitignore && chmod 644 .gitignore $ bd stealth fix # os.WriteFile succeeds
Defensive patterns
Strategy: try-catch
Validate before calling
if info, err := os.Stat(gitignorePath); err == nil {
if info.Mode().Perm()&0200 == 0 {
fmt.Printf("%s is not writable by current user; write will fail\n", gitignorePath)
}
} else if fi, err := os.Stat(filepath.Dir(gitignorePath)); err == nil && fi.Mode().Perm()&0200 == 0 {
fmt.Printf("directory %s is not writable; write will fail\n", filepath.Dir(gitignorePath))
} Try / catch
err := applyStealthFixes(...)
var perr *fs.PathError
if errors.As(err, &perr) && errors.Is(perr.Err, syscall.ENOSPC) {
fmt.Println("disk full while writing .gitignore; free space and retry")
} else if errors.As(err, &perr) && errors.Is(perr.Err, syscall.EACCES) {
fmt.Printf("permission denied writing %s; chown/chmod the file: %v\n", gitignorePath, err)
} Prevention
- Keep .gitignore owned by the running user with mode 0644.
- Monitor disk space/quotas on dev machines and CI runners.
- Pause file-sync/AV tools that briefly lock repo files during writes.
- Check SELinux/AppArmor policies if writes to project dirs are consistently denied.
When it happens
Trigger: os.WriteFile(gitignorePath, ...) returns an error: no write permission on .gitignore, no write permission on the containing directory (needed for truncate/rename semantics), read-only mount, ENOSPC (disk full), or .gitignore is actually a directory.
Common situations: .gitignore owned by root or another user while bd runs unprivileged; CI workspaces mounted read-only; disk quota/full disk on the dev machine; editor or sync tool (Dropbox/OneDrive) temporarily locking the file; SELinux/AppArmor denying writes to the repo path.
Related errors
- failed to read .gitignore: %w
- failed to remove emptied .gitignore: %w
- ensure .beads/.gitignore: %w
- failed to inspect target repo %s: %w
- read .beads/.gitignore: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/bdde1f5430cc243e.
Report an issue: GitHub.