gastownhall/beads · error

branch: from is required

Error message

branch: from is required

What it means

applyBranchesWithMap validates each ComposeRules branch rule and requires a From step ID identifying where the branch fan-out begins. An empty From makes the branch rule unusable, so ApplyBranches/ApplyControlFlow fails before modifying the step graph.

Source

Thrown at internal/formula/controlflow.go:446

	if err := applyBranchesWithMap(stepMap, compose); err != nil {
		return nil, err
	}

	return cloned, nil
}

// applyBranchesWithMap applies branch rules using a pre-built stepMap.
// This is the internal implementation used by both ApplyBranches and ApplyControlFlow.
// The stepMap entries are modified in place.
func applyBranchesWithMap(stepMap map[string]*Step, compose *ComposeRules) error {
	if compose == nil || len(compose.Branch) == 0 {
		return nil
	}

	for _, branch := range compose.Branch {
		// Validate the branch rule
		if branch.From == "" {
			return fmt.Errorf("branch: from is required")
		}
		if len(branch.Steps) == 0 {
			return fmt.Errorf("branch: steps is required")
		}
		if branch.Join == "" {
			return fmt.Errorf("branch: join is required")
		}

		// Verify all steps exist
		if _, ok := stepMap[branch.From]; !ok {
			return fmt.Errorf("branch: from step %q not found", branch.From)
		}
		if _, ok := stepMap[branch.Join]; !ok {
			return fmt.Errorf("branch: join step %q not found", branch.Join)
		}
		for _, stepID := range branch.Steps {
			if _, ok := stepMap[stepID]; !ok {
				return fmt.Errorf("branch: parallel step %q not found", stepID)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Add the missing `from:` key naming the fan-out step in the formula's branch rule
  2. Check YAML indentation so the branch entry actually contains its from field
  3. Ensure the field name matches the schema (from, not source/start)

Example fix

# before
branch:
  - steps: [a, b]
    join: c
# after
branch:
  - from: start
    steps: [a, b]
    join: c
Defensive patterns

Strategy: validation

Validate before calling

for i, b := range compose.Branch {
	if b.From == "" {
		return fmt.Errorf("branch[%d]: from is required", i)
	}
}

Try / catch

steps, err := formula.ApplyBranches(steps, compose)
if err != nil {
	if strings.Contains(err.Error(), "from is required") {
		return fmt.Errorf("compose branch missing 'from': %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Calling ApplyBranches(steps, compose) or ApplyControlFlow with a compose.Branch entry whose From field is empty/omitted in the formula's compose section.

Common situations: YAML branch block missing the `from:` key; indentation error placing the rule under the wrong parent; building ComposeRules programmatically and forgetting to set From.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


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