gastownhall/beads · error

applying gates: %w

Error message

applying gates: %w

What it means

ApplyControlFlow wraps failures from applyGatesWithMap with 'applying gates: %w'. Gate rules attach a 'gate:<condition>' label to a target step, evaluated at runtime by the patrol executor. This error means a gate rule is missing 'before' or 'condition', its condition fails ParseCondition syntax validation, or the 'before' target step ID does not exist.

Source

Thrown at internal/formula/controlflow.go:572

	// Apply loops first (expands steps) - ApplyLoops already returns new slice
	steps, err = ApplyLoops(steps)
	if err != nil {
		return nil, fmt.Errorf("applying loops: %w", err)
	}

	// Build stepMap once for branches and gates
	// No need to clone here since ApplyLoops already returned a new slice
	stepMap := buildStepMap(steps)

	// Apply branches (wires dependencies)
	if err := applyBranchesWithMap(stepMap, compose); err != nil {
		return nil, fmt.Errorf("applying branches: %w", err)
	}

	// Apply gates (adds labels)
	if err := applyGatesWithMap(stepMap, compose); err != nil {
		return nil, fmt.Errorf("applying gates: %w", err)
	}

	return steps, nil
}

// cloneStepDeep creates a deep copy of a step including children.
func cloneStepDeep(s *Step) *Step {
	clone := cloneStep(s)

	if len(s.Children) > 0 {
		clone.Children = make([]*Step, len(s.Children))
		for i, child := range s.Children {
			clone.Children[i] = cloneStepDeep(child)
		}
	}

	return clone
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped inner error to see which gate rule failed and why (missing field, invalid condition, or unknown target)
  2. Validate the condition syntax independently with ParseCondition(gate.Condition) and fix the expression
  3. Confirm the gate's Before step ID exists in the formula, including steps produced by loop expansion
  4. Correct or remove the invalid gate rule in the compose section

Example fix

// before
gates:
  - before: deploy
    condition: env ==
// after
gates:
  - before: deploy
    condition: env == "prod"
Defensive patterns

Strategy: validation

Validate before calling

for _, g := range compose.Gate {
	if g.Before == "" || g.Condition == "" {
		return fmt.Errorf("gate rule requires before and condition")
	}
	if _, err := ParseCondition(g.Condition); err != nil {
		return fmt.Errorf("gate condition %q invalid: %w", g.Condition, err)
	}
	if _, ok := buildStepMap(steps)[g.Before]; !ok {
		return fmt.Errorf("gate target %q not found", g.Before)
	}
}

Try / catch

out, err := ApplyControlFlow(steps, compose)
if err != nil {
	var gateErr error
	if strings.Contains(err.Error(), "applying gates") && errors.Unwrap(err) != nil {
		gateErr = errors.Unwrap(err)
	}
	return fmt.Errorf("gate rule rejected: %w", errors.Join(err, gateErr))
}

Prevention

When it happens

Trigger: Calling ApplyControlFlow(steps, compose) where a compose.Gate entry has an empty Before or Condition, a condition string that ParseCondition rejects, or a Before step ID absent from the step map.

Common situations: Malformed gate condition expressions in formula YAML (bad comparison syntax); gate targeting a step renamed or consumed by a prior expansion; copy-pasted gate rules referencing steps from a different formula.

Related errors


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