gastownhall/beads · error

fs: WriteProjectGitignore: workDir not set

Error message

fs: WriteProjectGitignore: workDir not set

What it means

WriteProjectGitignore manages bd-required patterns in the project root .gitignore. This sentinel error means the repository was built without a workDir, so bd has no project root to place the gitignore in. It is a wiring/configuration error, not a filesystem failure.

Source

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

	}
	var missing []string
	for _, line := range strings.Split(template, "\n") {
		trimmed := strings.TrimSpace(line)
		if trimmed == "" || strings.HasPrefix(trimmed, "#") || have[trimmed] {
			continue
		}
		missing = append(missing, trimmed)
	}
	return missing
}

func (r *beadsDirFSRepositoryImpl) BeadsGitignoreExists(ctx context.Context) (bool, error) {
	return fileExists(filepath.Join(r.beadsDir, ".gitignore"), "fs: BeadsGitignoreExists")
}

func (r *beadsDirFSRepositoryImpl) WriteProjectGitignore(ctx context.Context) error {
	if r.workDir == "" {
		return fmt.Errorf("fs: WriteProjectGitignore: workDir not set")
	}
	if len(r.templates.ProjectGitignorePatterns) == 0 {
		return fmt.Errorf("fs: WriteProjectGitignore: patterns not configured")
	}
	path := filepath.Join(r.workDir, ".gitignore")
	// #nosec G304 -- path joined under bound workDir
	existing, err := os.ReadFile(path)
	if err != nil && !errors.Is(err, os.ErrNotExist) {
		return fmt.Errorf("fs: WriteProjectGitignore: read: %w", err)
	}

	var toAdd []string
	for _, pattern := range r.templates.ProjectGitignorePatterns {
		if !containsLine(existing, pattern) {
			toAdd = append(toAdd, pattern)
		}
	}
	if len(toAdd) == 0 {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Construct the repository through its standard constructor, passing the project root/workDir option
  2. If wiring manually, set workDir to an absolute project path before calling the method
  3. Resolve the git root (git rev-parse --show-toplevel) and pass that as workDir when unsure
  4. Verify initialization order: workDir must be bound before any gitignore write is attempted

Example fix

// before
r := &beadsDirFSRepositoryImpl{beadsDir: dir} // workDir empty
r.WriteProjectGitignore(ctx) // workDir not set
// after
workDir, _ := gitRoot(ctx)
r := fsrepo.New(fsrepo.WithBeadsDir(dir), fsrepo.WithWorkDir(workDir))
r.WriteProjectGitignore(ctx)
Defensive patterns

Strategy: validation

Validate before calling

type worker interface{ HasWorkDir() bool }
if w, ok := repo.(worker); !ok || !w.HasWorkDir() {
	return errors.New("repo requires workDir before WriteProjectGitignore")
}
repo.WriteProjectGitignore(ctx)

Type guard

func hasWorkDir(w string) bool { return w != "" }

Try / catch

if err := repo.WriteProjectGitignore(ctx); err != nil {
	if strings.Contains(err.Error(), "workDir not set") {
		return fmt.Errorf("rebuild repository with fsrepo.WithWorkDir(gitRoot)")
	}
	return err
}

Prevention

When it happens

Trigger: Calling WriteProjectGitignore on a beadsDirFSRepositoryImpl whose workDir field is empty — the constructor was not given the project root.

Common situations: Programmatic use of the fs repository without WithWorkDir-style option; tests instantiating the impl directly; embedding bd as a library without resolving the project root first.

Related errors


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