gastownhall/beads · error
fs: WriteBeadsGitignore: %w
Error message
fs: WriteBeadsGitignore: %w
What it means
This wraps a failure from os.WriteFile when creating .beads/.gitignore for the first time (the file did not previously exist). bd could not write the new file with mode 0600, usually because the .beads directory is not writable or a non-directory entry blocks the path.
Source
Thrown at internal/storage/domain/fs/beads.go:96
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
if err != nil {
return false, fmt.Errorf("fs: BeadsDirExists: stat %s: %w", r.beadsDir, err)
}
return info.IsDir(), nil
}
func (r *beadsDirFSRepositoryImpl) WriteBeadsGitignore(ctx context.Context) error {
if r.templates.BeadsGitignore == "" {
return fmt.Errorf("fs: WriteBeadsGitignore: template not configured")
}
path := filepath.Join(r.beadsDir, ".gitignore")
// #nosec G304 -- path joined under bound beadsDir
existing, err := os.ReadFile(path)
if errors.Is(err, os.ErrNotExist) {
if werr := os.WriteFile(path, []byte(r.templates.BeadsGitignore), 0600); werr != nil {
return fmt.Errorf("fs: WriteBeadsGitignore: %w", werr)
}
return nil
}
if err != nil {
return fmt.Errorf("fs: WriteBeadsGitignore: read: %w", err)
}
// Existing file: append-only. A wholesale rewrite to the template
// destroys local rules (e.g. export negations) the user added — the
// gitignore is shared state, not bd-owned (bd-kaaz3).
missing := missingTemplatePatternLines(string(existing), r.templates.BeadsGitignore)
if len(missing) == 0 {
return nil
}
content := string(existing)
if len(content) > 0 && !strings.HasSuffix(content, "\n") {
content += "\n"
}
content += "\n# Added by bd (missing required patterns)\n" + strings.Join(missing, "\n") + "\n"View on GitHub (pinned to 71377f2769)
Solutions
- Ensure the .beads directory is writable by the current user (chown/chmod it)
- Remove any directory or stale entry occupying the .gitignore path inside .beads
- Free disk space / raise quota if the wrapped cause is ENOSPC
- If the volume is read-only, remount read-write or run bd from a writable checkout
Example fix
// before $ sudo chown root .beads $ bd init // fs: WriteBeadsGitignore: open .beads/.gitignore: permission denied // after $ sudo chown $(whoami) .beads $ bd init # .gitignore created
Defensive patterns
Strategy: validation
Validate before calling
if info, err := os.Stat(beadsDir); err == nil {
if err := unix.Access(beadsDir, unix.W_OK); err != nil {
return fmt.Errorf("%s not writable: %v", beadsDir, err)
}
if _, err := os.Lstat(filepath.Join(beadsDir, ".gitignore")); err == nil {
if fi, err := os.Stat(filepath.Join(beadsDir, ".gitignore")); err == nil && fi.IsDir() {
return errors.New(".beads/.gitignore is a directory")
}
}
}
repo.WriteBeadsGitignore(ctx) Type guard
func isWriteErr(err error) bool {
var pe *fs.PathError
return errors.As(err, &pe) && (pe.Op == "open" || pe.Op == "write")
} Try / catch
if err := repo.WriteBeadsGitignore(ctx); err != nil {
var pe *fs.PathError
if errors.As(err, &pe) && errors.Is(pe.Err, syscall.EACCES) {
return fmt.Errorf("make %s writable: chown -R $(whoami) %s", filepath.Dir(pe.Path), filepath.Dir(pe.Path))
}
return err
} Prevention
- Keep .beads writable by the CI/agent user
- Never mount .beads volumes read-only
- Avoid placing directories named .gitignore inside .beads
- Monitor disk quota on build agents
When it happens
Trigger: Calling WriteBeadsGitignore when .beads/.gitignore does not exist and os.WriteFile(path, ..., 0600) fails: permission denied on the .beads dir, path is a directory, disk full, or read-only filesystem.
Common situations: .beads mounted read-only (container volume); directory owned by another user; a directory named .gitignore sits in the way; quota exceeded on the volume.
Related errors
- failed to write .gitignore: %w
- ensure .beads/.gitignore: %w
- read .beads/.gitignore: %w
- failed to read .gitignore: %w
- failed to write user config.yaml: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/f39b0a6dfc4dc6b3.
Report an issue: GitHub.