{"record":{"id":"2169105bfb6ba920","repo":"apache/beam","slug":"empty-slice-passed-as-an-argument-to-trigger-afterany","errorCode":null,"errorMessage":"empty slice passed as an argument to trigger.AfterAny()","messagePattern":"empty slice passed as an argument to trigger\\.AfterAny\\(\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/graph/window/trigger/trigger.go","lineNumber":281,"sourceCode":"\tsubtriggers []Trigger\n}\n\n// String implements the Stringer interface and returns trigger details as a string.\nfunc (t *AfterAnyTrigger) String() string {\n\treturn fmt.Sprintf(\"%#v\", t)\n}\n\nfunc (t AfterAnyTrigger) trigger() {}\n\n// SubTriggers returns the component triggers.\nfunc (t *AfterAnyTrigger) SubTriggers() []Trigger {\n\treturn t.subtriggers\n}\n\n// AfterAny returns a new AfterAny trigger with subtriggers set to passed argument.\nfunc AfterAny(triggers []Trigger) *AfterAnyTrigger {\n\tif len(triggers) <= 1 {\n\t\tpanic(\"empty slice passed as an argument to trigger.AfterAny()\")\n\t}\n\treturn &AfterAnyTrigger{subtriggers: triggers}\n}\n\n// AfterAllTrigger fires after all subtriggers are fired.\ntype AfterAllTrigger struct {\n\tsubtriggers []Trigger\n}\n\n// String implements the Stringer interface and returns trigger details as a string.\nfunc (t *AfterAllTrigger) String() string {\n\treturn fmt.Sprintf(\"%#v\", t)\n}\n\nfunc (t AfterAllTrigger) trigger() {}\n\n// SubTriggers returns the component triggers.\nfunc (t *AfterAllTrigger) SubTriggers() []Trigger {","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/graph/window/trigger/trigger.go#L263-L299","documentation":"trigger.AfterAny() builds an AfterAnyTrigger that fires when any of its subtriggers fire. The library panics when the passed slice is empty or contains only one trigger, because an 'any' combination of fewer than two triggers is meaningless — a single trigger should be used directly. It is a deliberate fail-fast guard against degenerate trigger composition.","triggerScenarios":"Calling trigger.AfterAny(nil), trigger.AfterAny([]Trigger{}), or AfterAny with exactly one trigger (len <= 1).","commonSituations":"Building triggers programmatically from a slice accumulated at runtime (e.g. from config) where the slice ends up empty because no subtriggers were configured, or wrapping a single trigger 'for consistency'.","solutions":["Ensure at least two Trigger values are in the slice before calling AfterAny.","If the slice may be empty, fall back to a single trigger directly (AfterFirst/AfterEnd/etc.) or a default trigger.","If only one trigger is configured, return that trigger itself instead of wrapping it in AfterAny."],"exampleFix":"// before\ntr := trigger.AfterAny(subs) // panics when len(subs) <= 1\n// after\nvar tr trigger.Trigger\nswitch len(subs) {\ncase 0:\n\ttr = trigger.DefaultTrigger()\ncase 1:\n\ttr = subs[0]\ndefault:\n\ttr = trigger.AfterAny(subs)\n}","handlingStrategy":"validation","validationCode":"if len(subs) < 2 {\n\t// use a plain trigger or a default instead of AfterAny\n}\ntr := trigger.AfterAny(subs)","typeGuard":"func canAfterAny(ts []trigger.Trigger) bool { return len(ts) > 1 }","tryCatchPattern":null,"preventionTips":["Never call AfterAny/AfterAll with hardcoded short literals of fewer than 2 triggers.","When building trigger slices from config, decide the zero/one case explicitly.","Add a unit test covering the empty-config path of trigger construction."],"tags":["go","panic","beam","trigger","empty-slice"],"backgroundTag":"missing-required-argument","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}