gastownhall/beads · error

failed to write config.yaml: %w

Error message

failed to write config.yaml: %w

What it means

The final step of SetReposInYAML writes the re-encoded YAML back to config.yaml with os.WriteFile using mode 0600. If the write fails, the error is wrapped as "failed to write config.yaml". Note the write happens only after encode+close succeed, so the data itself is valid.

Source

Thrown at internal/config/repos.go:152

			&yaml.Node{Kind: yaml.ScalarNode, Value: "repos"},
			reposNode,
		)
	}

	// Marshal back to YAML
	var buf strings.Builder
	encoder := yaml.NewEncoder(&buf)
	encoder.SetIndent(2)
	if err := encoder.Encode(&root); err != nil {
		return fmt.Errorf("failed to encode config.yaml: %w", err)
	}
	if err := encoder.Close(); err != nil {
		return fmt.Errorf("failed to close encoder: %w", err)
	}

	// Write back to file
	if err := os.WriteFile(configPath, []byte(buf.String()), 0600); err != nil {
		return fmt.Errorf("failed to write config.yaml: %w", err)
	}

	// Reload viper config so changes take effect immediately
	if v != nil {
		if err := v.ReadInConfig(); err != nil {
			// Not fatal - config is on disk, will be picked up on next command
			_ = err // Best effort: viper reload failure is non-fatal since config was already written to disk
		}
	}

	return nil
}

// buildReposNode creates a yaml.Node for the repos configuration
// Returns nil if repos is empty (no primary and no additional)
func buildReposNode(repos *ReposConfig) *yaml.Node {
	if repos == nil || (repos.Primary == "" && len(repos.Additional) == 0) {
		return nil

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check permissions on config.yaml and its parent directory; chown/chmod as needed
  2. Free disk space or check quota (df -h, quota)
  3. Ensure the filesystem is writable (not a read-only mount)
  4. Delete and recreate the file if ownership is wrong (a new file gets correct owner)

Example fix

// before
// file owned by root, running as user -> permission denied
// after
sudo chown $(whoami) ~/.config/bd/config.yaml
bd repo add /path/to/repo
Defensive patterns

Strategy: validation

Validate before calling

d := filepath.Dir(configPath)
if fi, err := os.Stat(d); err != nil || !fi.IsDir() || fi.Mode().Perm()&0200 == 0 {
    return fmt.Errorf("config dir %s not writable", d)
}

Type guard

func isWritableDir(p string) bool {
    fi, err := os.Stat(p)
    return err == nil && fi.IsDir() && fi.Mode().Perm()&0200 != 0
}

Try / catch

if err := SetReposInYAML(cfgPath, repos); err != nil {
    if strings.Contains(err.Error(), "failed to write") {
        log.Printf("check ownership/permissions/disk on %s: %v", cfgPath, err)
    }
    return err
}

Prevention

When it happens

Trigger: os.WriteFile(configPath, ...) fails — directory not writable, disk full, permission denied (existing file owned by another user — WriteFile does not chown), or read-only filesystem.

Common situations: Running bd as a different user than the one who owns config.yaml (0600 means only the owner can write), disk quota exceeded, or config directory mounted read-only (e.g. container with read-only volume).

Related errors


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