gastownhall/beads · error

parse recipes.toml: %w

Error message

parse recipes.toml: %w

What it means

The user's recipes.toml was read successfully but could not be parsed as TOML. The toml.Unmarshal error is wrapped so you can see the malformed line from the original parser message.

Source

Thrown at internal/recipes/recipes.go:159

// 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
	}

	return userRecipes.Recipes, nil
}

// GetAllRecipes returns merged built-in and user recipes.
// User recipes override built-in recipes with the same name.

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped TOML error for the exact line/column and fix the syntax in .beads/recipes.toml
  2. Validate the file with a TOML linter or `tomlq . .beads/recipes.toml`
  3. Compare against a known-good recipes.toml structure ([recipes.<name>] with name/path/type fields)
  4. Restore the file from backup or delete it to fall back to built-in recipes

Example fix

// before (broken)
[recipes.myrecipe
name = "x"
// after
[recipes.myrecipe]
name = "x"
Defensive patterns

Strategy: validation

Validate before calling

// Validate TOML before handing it to the app
var probe map[string]any
if err := toml.Unmarshal(data, &probe); err != nil {
	return fmt.Errorf("recipes.toml invalid: %w", err)
}

Try / catch

userRecipes, err := LoadUserRecipes(ctx, beadsDir)
if err != nil && strings.HasPrefix(err.Error(), "parse recipes.toml") {
	// fall back to built-ins, surface the parse error to the user
	warn(err)
	userRecipes = nil
}

Prevention

When it happens

Trigger: toml.Unmarshal fails on the contents of <beadsDir>/recipes.toml — invalid TOML syntax, wrong types for Recipe fields, or an unexpected top-level structure.

Common situations: Hand-edited recipes.toml with a missing quote, duplicate key, tabs in wrong place, or a value of the wrong type (e.g. a string where a map of recipes is expected); file truncated by a crashed editor.

Related errors


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