{"record":{"id":"aee607b313e48a2f","repo":"gastownhall/beads","slug":"gate-condition-is-required","errorCode":null,"errorMessage":"gate: condition is required","messagePattern":"gate: condition is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/formula/controlflow.go","lineNumber":521,"sourceCode":"\n\treturn cloned, nil\n}\n\n// applyGatesWithMap applies gate rules using a pre-built stepMap.\n// This is the internal implementation used by both ApplyGates and ApplyControlFlow.\n// The stepMap entries are modified in place.\nfunc applyGatesWithMap(stepMap map[string]*Step, compose *ComposeRules) error {\n\tif compose == nil || len(compose.Gate) == 0 {\n\t\treturn nil\n\t}\n\n\tfor _, gate := range compose.Gate {\n\t\t// Validate the gate rule\n\t\tif gate.Before == \"\" {\n\t\t\treturn fmt.Errorf(\"gate: before is required\")\n\t\t}\n\t\tif gate.Condition == \"\" {\n\t\t\treturn fmt.Errorf(\"gate: condition is required\")\n\t\t}\n\n\t\t// Validate the condition syntax\n\t\t_, err := ParseCondition(gate.Condition)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"gate: invalid condition %q: %w\", gate.Condition, err)\n\t\t}\n\n\t\t// Find the target step\n\t\tstep, ok := stepMap[gate.Before]\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"gate: target step %q not found\", gate.Before)\n\t\t}\n\n\t\t// Add gate label for runtime evaluation using JSON for unambiguous parsing\n\t\tgateMeta := map[string]string{\"condition\": gate.Condition}\n\t\tgateJSON, _ := json.Marshal(gateMeta)\n\t\tgateLabel := fmt.Sprintf(\"gate:%s\", string(gateJSON))","sourceCodeStart":503,"sourceCodeEnd":539,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/formula/controlflow.go#L503-L539","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Add a valid condition expression to the gate rule, e.g. condition: \"label:critical\" or a field comparison","If the condition value comes from a template/variable, verify it is non-empty at compose time","Remove the gate entry entirely if no condition is intended"],"exampleFix":"// before (compose rules)\ngate: [{before: deploy}]\n// after\ngate: [{before: deploy, condition: \"label:critical\"}]","handlingStrategy":"validation","validationCode":"func validateGateConditions(compose *ComposeRules) error {\n\tfor i, g := range compose.Gate {\n\t\tif g.Condition == \"\" {\n\t\t\treturn fmt.Errorf(\"gate[%d]: missing required field 'condition'\", i)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := formula.ApplyGates(steps, compose); err != nil {\n\tif strings.Contains(err.Error(), \"gate: condition is required\") {\n\t\treturn fmt.Errorf(\"workflow config error: gate on %q has no condition; add one or remove the gate\", gateTarget(compose))\n\t}\n\treturn err\n}","preventionTips":["Never add a gate entry without a condition expression","If conditions come from templates/variables, validate they are non-empty before compose","Mark gate.condition required in any config schema or linter","Delete gate entries entirely instead of leaving them with an empty condition"],"tags":["go","formula","workflow-validation","missing-field"],"backgroundTag":"missing-required-field","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}