{"record":{"id":"39b8296a01809a13","repo":"gastownhall/beads","slug":"gate-target-step-q-not-found","errorCode":null,"errorMessage":"gate: target step %q not found","messagePattern":"gate: target step %q not found","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/formula/controlflow.go","lineNumber":533,"sourceCode":"\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))\n\t\tstep.Labels = appendUnique(step.Labels, gateLabel)\n\t}\n\n\treturn nil\n}\n\n// ApplyControlFlow applies all control flow operators in the correct order:\n// 1. Loops (expand iterations)\n// 2. Branches (wire fork-join dependencies)\n// 3. Gates (add condition labels)\n//\n// Returns a new steps slice. The original steps slice is not modified.","sourceCodeStart":515,"sourceCodeEnd":551,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/formula/controlflow.go#L515-L551","documentation":"After validating a gate's condition, applyGatesWithMap looks up Gate[i].Before in the workflow's step map to attach the gate label to that step. This error is returned when the target step ID does not exist in the steps list. The gate can only guard a step the workflow actually defines, so the library fails with the offending ID.","triggerScenarios":"Calling ApplyGates or ApplyControlFlow with a gate whose Before field names a step absent from the steps slice — a typo, a renamed/deleted step, a step created by loop expansion under a different ID, or an ID with case/whitespace mismatch.","commonSituations":"Config referencing a step that was renamed in a refactor; gate written against a step in a different workflow file; copy-pasted gate rules not updated for the new workflow's step IDs; steps removed by an earlier edit while gates still point at them.","solutions":["Correct the gate's `before` value to match an existing step ID exactly","Confirm the target step is still defined and wasn't renamed or removed in a recent edit","Check for exact-match issues (case, whitespace) between the gate's before value and the step ID","If the step is produced by loop expansion, use the expanded step ID or gate the loop's source step"],"exampleFix":"// before (compose rules)\ngate: [{before: deploy-prod, condition: \"label:critical\"}]\n// steps define \"deploy\" only\n// after\ngate: [{before: deploy, condition: \"label:critical\"}]","handlingStrategy":"validation","validationCode":"func validateGateTargets(steps []*Step, compose *ComposeRules) error {\n\tids := make(map[string]bool, len(steps))\n\tfor _, s := range steps {\n\t\tids[s.ID] = true\n\t}\n\tfor _, g := range compose.Gate {\n\t\tif g.Before != \"\" && !ids[g.Before] {\n\t\t\treturn fmt.Errorf(\"gate targets unknown step %q\", g.Before)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := formula.ApplyControlFlow(steps, compose); err != nil {\n\tvar target string\n\tif n, _ := fmt.Sscanf(err.Error(), \"gate: target step %q not found\", &target); n == 1 {\n\t\treturn fmt.Errorf(\"workflow config error: gate targets unknown step %q; defined steps: %v\", target, stepIDs(steps))\n\t}\n\treturn err\n}","preventionTips":["Reference gate targets from the same ID list that defines steps, not by hand","Re-check gate rules whenever steps are renamed or removed","Match step IDs exactly — no case or whitespace drift","Run a config lint that cross-checks gate.before against defined step IDs before execution"],"tags":["go","formula","workflow-validation","step-not-found"],"backgroundTag":"referenced-step-not-found","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}