gastownhall/beads · error

read recipes.toml: %w

Error message

read recipes.toml: %w

What it means

LoadUserRecipes failed to read the user's recipes.toml from the .beads directory for a reason other than file-not-found (which is treated as 'no user recipes'). The underlying os.ReadFile error is wrapped for diagnostics.

Source

Thrown at internal/recipes/recipes.go:154

		Description: "Junie guidelines and MCP configuration",
		Paths:       []string{".junie/guidelines.md", ".junie/mcp/mcp.json"},
	},
}

// UserRecipes holds recipes loaded from user config file.
type UserRecipes struct {
	Recipes map[string]Recipe `toml:"recipes"`
}

// LoadUserRecipes loads recipes from .beads/recipes.toml if it exists.
func LoadUserRecipes(beadsDir string) (map[string]Recipe, error) {
	path := filepath.Join(beadsDir, "recipes.toml")
	data, err := os.ReadFile(path) // #nosec G304 -- path is constructed from validated beadsDir
	if os.IsNotExist(err) {
		return nil, nil // No user recipes, that's fine
	}
	if err != nil {
		return nil, fmt.Errorf("read recipes.toml: %w", err)
	}

	var userRecipes UserRecipes
	if err := toml.Unmarshal(data, &userRecipes); err != nil {
		return nil, fmt.Errorf("parse recipes.toml: %w", err)
	}

	// Set defaults for user recipes
	for name, recipe := range userRecipes.Recipes {
		if recipe.Type == "" {
			recipe.Type = TypeFile
		}
		if recipe.Name == "" {
			recipe.Name = name
		}
		userRecipes.Recipes[name] = recipe
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check file permissions: `ls -l .beads/recipes.toml` and ensure the current user can read it (chmod 600/644)
  2. Verify the path is a regular file, not a directory: `file .beads/recipes.toml`
  3. Fix ownership if it was created by another user: `sudo chown $USER .beads/recipes.toml`
  4. If the file is unneeded/corrupt beyond repair, remove or rename it so loading proceeds with built-in recipes

Example fix

// before
-rw------- root root .beads/recipes.toml
// after
chown $USER .beads/recipes.toml && chmod 600 .beads/recipes.toml
Defensive patterns

Strategy: try-catch

Validate before calling

info, err := os.Stat(filepath.Join(beadsDir, "recipes.toml"))
if err == nil && !info.Mode().IsRegular() {
	return fmt.Errorf("recipes.toml is not a regular file")
}
if err == nil && info.Mode().Perm()&0o400 == 0 {
	return fmt.Errorf("recipes.toml is not readable by current user")
}

Try / catch

recipes, err := recipes.LoadUserRecipes(ctx, beadsDir)
if err != nil {
	if os.IsPermission(errors.Unwrap(err)) {
		// fall back to built-in recipes and warn
		recipes = nil
	} else {
		return err
	}
}

Prevention

When it happens

Trigger: os.ReadFile on <beadsDir>/recipes.toml returns a non-IsNotExist error: permission denied, path is a directory, I/O error, or too many symlinks.

Common situations: recipes.toml exists but is unreadable due to restrictive file permissions, ownership issues after running as another user (root vs user), the path being a directory, or a failing disk/NFS mount.

Related errors


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