gastownhall/beads · error

fs: WriteBeadsGitignore: template not configured

Error message

fs: WriteBeadsGitignore: template not configured

What it means

WriteBeadsGitignore writes bd's required ignore patterns into .beads/.gitignore. This sentinel error means the repository was constructed without the BeadsGitignore template configured, so there is nothing to write. It is a programming/configuration error in the fs repository wiring, not a filesystem problem.

Source

Thrown at internal/storage/domain/fs/beads.go:89

		return fmt.Errorf("fs: CreateBeadsDir: fix perms %s: %w", r.beadsDir, err)
	}
	return nil
}

func (r *beadsDirFSRepositoryImpl) BeadsDirExists(ctx context.Context) (bool, error) {
	info, err := os.Stat(r.beadsDir)
	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 {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Construct the repository through its normal constructor/factory so the embedded templates are loaded
  2. If constructing manually, set templates.BeadsGitignore to the bundled template content before calling the method
  3. Check embed directives/assets are present in the build (no stubbed template source)
  4. Update out-of-date local code that predates the templates field requirement

Example fix

// before
r := &beadsDirFSRepositoryImpl{beadsDir: dir} // templates zero-value
r.WriteBeadsGitignore(ctx) // template not configured
// after
r := fsrepo.New(fsrepo.WithBeadsDir(dir), fsrepo.WithDefaultTemplates())
r.WriteBeadsGitignore(ctx)
Defensive patterns

Strategy: validation

Validate before calling

type templated interface{ Templates() Templates }
if t, ok := repo.(templated); ok && t.Templates().BeadsGitignore == "" {
	return errors.New("repo built without BeadsGitignore template")
}
repo.WriteBeadsGitignore(ctx)

Type guard

func hasGitignoreTemplate(t Templates) bool { return t.BeadsGitignore != "" }

Try / catch

if err := repo.WriteBeadsGitignore(ctx); err != nil {
	if strings.Contains(err.Error(), "template not configured") {
		return fmt.Errorf("repository misconfigured: rebuild via fsrepo.New with default templates")
	}
	return err
}

Prevention

When it happens

Trigger: Calling WriteBeadsGitignore on a beadsDirFSRepositoryImpl whose templates.BeadsGitignore field is empty — i.e. the repository was constructed without providing the embedded template set.

Common situations: Custom repository construction that forgot to pass the templates/embedded assets; tests instantiating the impl directly with a zero-value templates struct; a build tag or embed failure leaving templates empty.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/98546db33ce788fe. Report an issue: GitHub.