apache/beam · critical

Failed to optimize MergeAccumulators for combiner

Error message

Failed to optimize MergeAccumulators for combiner %v. Failed to infer types

What it means

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.

Solutions

  1. 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.
  2. Pass the correct type parameters: accumulator type first for Combiner2/Combiner3 (register.Combiner2[AccT, InT]).
  3. Register the combiner as the same value/receiver kind its methods are defined on (usually &MyCombiner{}).
  4. If MergeAccumulators legitimately needs a different shape, fall back to the non-optimized registration (register.Combiner without generics / older API).
  5. Double-check named types vs aliases: ensure the accumulator field/method types are identical to T, not a distinct named type.

Example fix

// before: accumulator type doesn't match type parameter
type AvgCombiner struct{}
func (fn *AvgCombiner) MergeAccumulators(a, b float64) float64 { return a + b }
register.Combiner1[int](&AvgCombiner{})

// after: match types
register.Combiner1[float64](&AvgCombiner{}) // MergeAccumulators is func(float64, float64) float64
Defensive patterns

Strategy: type-guard

Validate before calling

// Verify the combiner's MergeAccumulators matches the generic argument before calling CombinerN:
func mergeIsOptimizable[T any](accum any) bool {
    _, ok1 := accum.(interface{ MergeAccumulators(T, T) (T, error) })
    _, ok2 := accum.(interface{ MergeAccumulators(T, T) T })
    return ok1 || ok2
}
// usage: if !mergeIsOptimizable[float64](c) { fall back to non-optimized registration }

Type guard

func isMergeAccumulators2x1[T any](accum any) bool {
    _, ok := accum.(interface {
        MergeAccumulators(T, T) T
    })
    return ok
}

Try / catch

func registerCombiner[T any](accum any) {
    defer func() {
        if r := recover(); r != nil {
            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)
        }
    }()
    register.Combiner1[T](accum)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/9ab25c9371ec8ba5. Report an issue: GitHub.

Appendix: source

Thrown at sdks/go/pkg/beam/register/register.tmpl:333

				return fn.(mergeAccumulators2x2[T0]).MergeAccumulators(a0, a1)
			})
		}
    } else if _, ok := accum.(mergeAccumulators2x1[T0]); ok {
        caller := func(fn any) reflectx.Func {
            f := fn.(func(T0, T0) T0)
            return &caller2x1[T0, T0, T0]{fn: f}
        }
		reflectx.RegisterFunc(reflect.TypeOf((*func(T0, T0) T0)(nil)).Elem(), caller)

        mergeAccumulatorsWrapper = func(fn any) reflectx.Func {
			return reflectx.MakeFunc(func(a0 T0, a1 T0) T0 {
				return fn.(mergeAccumulators2x1[T0]).MergeAccumulators(a0, a1)
			})
		}
    }

    if mergeAccumulatorsWrapper == nil {
        panic(fmt.Sprintf("Failed to optimize MergeAccumulators for combiner %v. Failed to infer types", accum))
    }

    var createAccumulatorWrapper func(fn any) reflectx.Func
    if _, ok := accum.(createAccumulator0x2[T0]); ok {
        caller := func(fn any) reflectx.Func {
            f := fn.(func() (T0, error))
            return &caller0x2[T0, error]{fn: f}
        }
		reflectx.RegisterFunc(reflect.TypeOf((*func() (T0, error))(nil)).Elem(), caller)

        createAccumulatorWrapper = func(fn any) reflectx.Func {
			return reflectx.MakeFunc(func() (T0, error) {
				return fn.(createAccumulator0x2[T0]).CreateAccumulator()
			})
		}
    } else if _, ok := accum.(createAccumulator0x1[T0]); ok {
        caller := func(fn any) reflectx.Func {
            f := fn.(func() T0)

View on GitHub (pinned to 12126d8942)