{"record":{"id":"ef7551e96799c35f","repo":"gastownhall/beads","slug":"gate-before-is-required","errorCode":null,"errorMessage":"gate: before is required","messagePattern":"gate: before is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/formula/controlflow.go","lineNumber":518,"sourceCode":"\tif err := applyGatesWithMap(stepMap, compose); err != nil {\n\t\treturn nil, err\n\t}\n\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","sourceCodeStart":500,"sourceCodeEnd":536,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/formula/controlflow.go#L500-L536","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Add the `before` field to the gate rule with the ID of the step the gate should guard","If the gate is unnecessary, remove the empty gate entry from the compose rules","Check the config file for a blank `before:` key left behind by an edit or merge"],"exampleFix":"// before (compose rules)\ngate: [{condition: \"label:critical\"}]\n// after\ngate: [{before: deploy, condition: \"label:critical\"}]","handlingStrategy":"validation","validationCode":"func validateGateRules(compose *ComposeRules) error {\n\tfor i, g := range compose.Gate {\n\t\tif g.Before == \"\" {\n\t\t\treturn fmt.Errorf(\"gate[%d]: missing required field 'before'\", 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: before is required\") {\n\t\treturn fmt.Errorf(\"workflow config error: every gate rule needs a 'before' step; check gate entries in compose rules\")\n\t}\n\treturn err\n}","preventionTips":["Always fill both `before` and `condition` when adding a gate entry","Use a schema/validator for workflow config that marks gate.before as required","Never construct GateRule structs without initializing Before","Review gate blocks after config merges for dropped fields"],"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"}