{"record":{"id":"9ab25c9371ec8ba5","repo":"apache/beam","slug":"failed-to-optimize-mergeaccumulators-for-combiner-v-failed-9ab25c","errorCode":null,"errorMessage":"Failed to optimize MergeAccumulators for combiner %v. Failed to infer types","messagePattern":"Failed to optimize MergeAccumulators for combiner (.+?)\\. Failed to infer types","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"sdks/go/pkg/beam/register/register.tmpl","lineNumber":333,"sourceCode":"\t\t\t\treturn fn.(mergeAccumulators2x2[T0]).MergeAccumulators(a0, a1)\n\t\t\t})\n\t\t}\n    } else if _, ok := accum.(mergeAccumulators2x1[T0]); ok {\n        caller := func(fn any) reflectx.Func {\n            f := fn.(func(T0, T0) T0)\n            return &caller2x1[T0, T0, T0]{fn: f}\n        }\n\t\treflectx.RegisterFunc(reflect.TypeOf((*func(T0, T0) T0)(nil)).Elem(), caller)\n\n        mergeAccumulatorsWrapper = func(fn any) reflectx.Func {\n\t\t\treturn reflectx.MakeFunc(func(a0 T0, a1 T0) T0 {\n\t\t\t\treturn fn.(mergeAccumulators2x1[T0]).MergeAccumulators(a0, a1)\n\t\t\t})\n\t\t}\n    }\n\n    if mergeAccumulatorsWrapper == nil {\n        panic(fmt.Sprintf(\"Failed to optimize MergeAccumulators for combiner %v. Failed to infer types\", accum))\n    }\n\n    var createAccumulatorWrapper func(fn any) reflectx.Func\n    if _, ok := accum.(createAccumulator0x2[T0]); ok {\n        caller := func(fn any) reflectx.Func {\n            f := fn.(func() (T0, error))\n            return &caller0x2[T0, error]{fn: f}\n        }\n\t\treflectx.RegisterFunc(reflect.TypeOf((*func() (T0, error))(nil)).Elem(), caller)\n\n        createAccumulatorWrapper = func(fn any) reflectx.Func {\n\t\t\treturn reflectx.MakeFunc(func() (T0, error) {\n\t\t\t\treturn fn.(createAccumulator0x2[T0]).CreateAccumulator()\n\t\t\t})\n\t\t}\n    } else if _, ok := accum.(createAccumulator0x1[T0]); ok {\n        caller := func(fn any) reflectx.Func {\n            f := fn.(func() T0)","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/register/register.tmpl#L315-L351","documentation":"Raised by register.Combiner1/2/3 in Apache Beam Go when the passed CombineFn has a MergeAccumulators method, but none of the generated generic type assertions (mergeAccumulators2x2[T0] returning (T0, error) or mergeAccumulators2x1[T0] returning T0) match it. The framework can't build the optimized (reflection-free) merge wrapper for the given type parameter T, so it panics. This optimization path requires the merge method to operate on the declared accumulator type exactly.","triggerScenarios":"Calling register.Combiner1[T](&MyCombiner{}) (or Combiner2/Combiner3) where MyCombiner.MergeAccumulators does not have signature func(T, T) T or func(T, T) (T, error) for the supplied type argument T — e.g. wrong type parameter supplied, accumulator type differs from T, or the method takes/returns interface or non-generic concrete types that don't unify with T0.","commonSituations":"Supplying the wrong type parameter (Combiner2[A,B] with mismatched accumulator type); combining on aliases or named types that don't match T; a combiner written before Beam's generics-based registration API being registered via the new optimized path; passing a struct (not pointer) or a value whose methods have value vs pointer receivers mismatching the assertion.","solutions":["Make MergeAccumulators exactly func(a, b T) T or func(a, b T) (T, error) where T is the type argument passed to Combiner1/2/3.","Pass the correct type parameters: accumulator type first for Combiner2/Combiner3 (register.Combiner2[AccT, InT]).","Register the combiner as the same value/receiver kind its methods are defined on (usually &MyCombiner{}).","If MergeAccumulators legitimately needs a different shape, fall back to the non-optimized registration (register.Combiner without generics / older API).","Double-check named types vs aliases: ensure the accumulator field/method types are identical to T, not a distinct named type."],"exampleFix":"// before: accumulator type doesn't match type parameter\ntype AvgCombiner struct{}\nfunc (fn *AvgCombiner) MergeAccumulators(a, b float64) float64 { return a + b }\nregister.Combiner1[int](&AvgCombiner{})\n\n// after: match types\nregister.Combiner1[float64](&AvgCombiner{}) // MergeAccumulators is func(float64, float64) float64","handlingStrategy":"type-guard","validationCode":"// Verify the combiner's MergeAccumulators matches the generic argument before calling CombinerN:\nfunc mergeIsOptimizable[T any](accum any) bool {\n    _, ok1 := accum.(interface{ MergeAccumulators(T, T) (T, error) })\n    _, ok2 := accum.(interface{ MergeAccumulators(T, T) T })\n    return ok1 || ok2\n}\n// usage: if !mergeIsOptimizable[float64](c) { fall back to non-optimized registration }","typeGuard":"func isMergeAccumulators2x1[T any](accum any) bool {\n    _, ok := accum.(interface {\n        MergeAccumulators(T, T) T\n    })\n    return ok\n}","tryCatchPattern":"func registerCombiner[T any](accum any) {\n    defer func() {\n        if r := recover(); r != nil {\n            log.Fatalf(\"combiner %T not registrable: check MergeAccumulators matches func(T,T)(T) or func(T,T)(T,error) for T=%T: %v\", accum, *new(T), r)\n        }\n    }()\n    register.Combiner1[T](accum)\n}","preventionTips":["Keep MergeAccumulators as func(a, b T) T or func(a, b T) (T, error) with T equal to the generic argument.","For Combiner2/3, always list the accumulator type first in the type parameters.","Use pointer receivers consistently and register &MyCombiner{}.","Avoid named types/aliases that differ from the declared generic type argument.","Unit-test CombinerN registration for every combiner in CI."],"tags":["go","apache-beam","panics","generics","combiner"],"backgroundTag":"type-mismatch","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}