gastownhall/beads · error

gate: condition is required

Error message

gate: condition is required

What it means

applyGatesWithMap requires every gate rule to carry a non-empty `condition`, which is evaluated at runtime by the patrol executor to decide whether the gated step runs. This error is returned when Gate[i].Condition is empty. A gate without a condition is meaningless, so the library rejects it before adding labels.

Source

Thrown at internal/formula/controlflow.go:521

	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
		gateMeta := map[string]string{"condition": gate.Condition}
		gateJSON, _ := json.Marshal(gateMeta)
		gateLabel := fmt.Sprintf("gate:%s", string(gateJSON))

View on GitHub (pinned to 71377f2769)

Solutions

  1. Add a valid condition expression to the gate rule, e.g. condition: "label:critical" or a field comparison
  2. If the condition value comes from a template/variable, verify it is non-empty at compose time
  3. Remove the gate entry entirely if no condition is intended

Example fix

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

Strategy: validation

Validate before calling

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

Try / catch

if _, err := formula.ApplyGates(steps, compose); err != nil {
	if strings.Contains(err.Error(), "gate: condition is required") {
		return fmt.Errorf("workflow config error: gate on %q has no condition; add one or remove the gate", gateTarget(compose))
	}
	return err
}

Prevention

When it happens

Trigger: Calling ApplyGates or ApplyControlFlow with a ComposeRules whose Gate[i].Condition is the empty string — e.g. `before:` was filled in but `condition:` was omitted, left blank, or set from an empty environment/variable substitution.

Common situations: Workflow config where the `condition:` key was deleted or never written; templated configs where the condition comes from a variable that expanded to empty; partial copy-paste of a gate block.

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