gastownhall/beads · error

formula %q not found in search paths

Error message

formula %q not found in search paths

What it means

loadFormula() searched every configured search path for a formula file matching the requested name and found nothing (os.Stat failed for each candidate path). It is the library's canonical 'no such formula' error, exposed publicly through LoadByName for expansion operators.

Source

Thrown at internal/formula/parser.go:303

// Tries TOML first (.formula.toml), then falls back to JSON (.formula.json).
func (p *Parser) loadFormula(name string) (*Formula, error) {
	// Check cache first
	if cached, ok := p.cache[name]; ok {
		return cached, nil
	}

	// Search for the formula file - try TOML first, then JSON
	extensions := []string{FormulaExtTOML, FormulaExtJSON}
	for _, dir := range p.searchPaths {
		for _, ext := range extensions {
			path := filepath.Join(dir, name+ext)
			if _, err := os.Stat(path); err == nil {
				return p.ParseFile(path)
			}
		}
	}

	return nil, fmt.Errorf("formula %q not found in search paths", name)
}

// LoadByName loads a formula by name from search paths.
// This is the public API for loading formulas used by expansion operators.
func (p *Parser) LoadByName(name string) (*Formula, error) {
	return p.loadFormula(name)
}

// mergeSteps merges child steps into parent steps.
// Child steps with the same ID as a parent step replace the parent step
// in-place (preserving position). Child steps with new IDs are appended.
func mergeSteps(parent, child []*Step) []*Step {
	// Index parent steps by ID for quick lookup
	parentIdx := make(map[string]int, len(parent))
	for i, s := range parent {
		parentIdx[s.ID] = i
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify the formula file exists and its name matches exactly (case-sensitive) what you passed to LoadByName or put in extends.
  2. Add the formula's directory to the parser's search paths when constructing the Parser.
  3. List the search-path directories and the formulas in them to confirm the expected file is present and named per convention.

Example fix

// before
p := NewParser(WithSearchPaths("./formulas"))
f, err := p.LoadByName("deploy") // file is in ./molecules/deploy.md

// after
p := NewParser(WithSearchPaths("./formulas", "./molecules"))
f, err := p.LoadByName("deploy")
Defensive patterns

Strategy: validation

Validate before calling

// Verify the formula file exists in a search path before LoadByName.
func formulaExists(name string, searchPaths []string) bool {
	for _, dir := range searchPaths {
		if _, err := os.Stat(filepath.Join(dir, name+".md")); err == nil {
			return true
		}
	}
	return false
}

Try / catch

f, err := p.LoadByName(name)
if err != nil {
	if strings.Contains(err.Error(), "not found in search paths") {
		// suggest closest matches by listing the search dirs
	}
}

Prevention

When it happens

Trigger: Parser.LoadByName("name") with no matching formula file in the search paths; Resolve on a formula whose extends entry names a nonexistent parent (then wrapped by 1741); expansion operators resolving a formula reference that does not exist.

Common situations: Typos in the formula name; formula file never created; file lives outside the configured search paths; wrong file extension or naming convention so the Stat candidate paths never match; working from a different repo checkout where the formula directory is absent.

Related errors


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