gastownhall/beads · error

gate: before is required

Error message

gate: before is required

What it means

applyGatesWithMap requires every gate rule to specify a `before` step — the step that gets the gate condition label and is conditionally skipped at runtime. This error is returned when a gate entry in ComposeRules has an empty Before field. The library refuses to apply a gate with no target because the condition would have nothing to attach to.

Source

Thrown at internal/formula/controlflow.go:518

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

	return cloned, nil
}

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

	for _, gate := range compose.Gate {
		// Validate the gate rule
		if gate.Before == "" {
			return fmt.Errorf("gate: before is required")
		}
		if gate.Condition == "" {
			return fmt.Errorf("gate: condition is required")
		}

		// Validate the condition syntax
		_, err := ParseCondition(gate.Condition)
		if err != nil {
			return fmt.Errorf("gate: invalid condition %q: %w", gate.Condition, err)
		}

		// Find the target step
		step, ok := stepMap[gate.Before]
		if !ok {
			return fmt.Errorf("gate: target step %q not found", gate.Before)
		}

		// Add gate label for runtime evaluation using JSON for unambiguous parsing

View on GitHub (pinned to 71377f2769)

Solutions

  1. Add the `before` field to the gate rule with the ID of the step the gate should guard
  2. If the gate is unnecessary, remove the empty gate entry from the compose rules
  3. Check the config file for a blank `before:` key left behind by an edit or merge

Example fix

// before (compose rules)
gate: [{condition: "label:critical"}]
// after
gate: [{before: deploy, condition: "label:critical"}]
Defensive patterns

Strategy: validation

Validate before calling

func validateGateRules(compose *ComposeRules) error {
	for i, g := range compose.Gate {
		if g.Before == "" {
			return fmt.Errorf("gate[%d]: missing required field 'before'", i)
		}
	}
	return nil
}

Try / catch

if _, err := formula.ApplyGates(steps, compose); err != nil {
	if strings.Contains(err.Error(), "gate: before is required") {
		return fmt.Errorf("workflow config error: every gate rule needs a 'before' step; check gate entries in compose rules")
	}
	return err
}

Prevention

When it happens

Trigger: Calling ApplyGates or ApplyControlFlow with a ComposeRules whose Gate[i].Before is the empty string — e.g. a gate entry created without the before field, or one unmarshaled from config where `before:` was left blank.

Common situations: YAML/JSON workflow config where the `before:` key under a gate was omitted or left empty after editing; programmatically constructed GateRule structs missing field initialization; merging config files that dropped the field.

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/ef7551e96799c35f. Report an issue: GitHub.