apache/beam · critical

Failed to optimize CreateAccumulator for combiner %v. Failed

Error message

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

What it means

Raised by register.Combiner1/2/3 when the combiner value exposes a CreateAccumulator method (found via reflect.MethodByName) but the generated type assertions — createAccumulator0x2[T0] as func() (T0, error) and createAccumulator0x1[T0] as func() T0 — both failed. Beam could not build the optimized creation wrapper for the declared accumulator type T0, so it panics rather than silently using a slow path. Note the guard: it only fires when the method exists but isn't type-inferable.

Source

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

			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)
            return &caller0x1[T0]{fn: f}
        }
		reflectx.RegisterFunc(reflect.TypeOf((*func() T0)(nil)).Elem(), caller)

        createAccumulatorWrapper = func(fn any) reflectx.Func {
			return reflectx.MakeFunc(func() T0 {
				return fn.(createAccumulator0x1[T0]).CreateAccumulator()
			})
		}
    }
    if m := accumVal.MethodByName("CreateAccumulator"); m.IsValid() && createAccumulatorWrapper == nil {
        panic(fmt.Sprintf("Failed to optimize CreateAccumulator for combiner %v. Failed to infer types", accum))
    }

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

        addInputWrapper = func(fn any) reflectx.Func {
			return reflectx.MakeFunc(func(a0 T0, a1 T0) (T0, error) {
				return fn.(addInput2x2[T0, T0]).AddInput(a0, a1)
			})
		}
    } else if _, ok := accum.(addInput2x1[T0, T0]); ok {
        caller := func(fn any) reflectx.Func {
            f := fn.(func(T0, T0) T0)

View on GitHub (pinned to 12126d8942)

Solutions

  1. Make CreateAccumulator exactly func() T0 or func() (T0, error) where T0 is the accumulator type argument you passed to Combiner1/2/3.
  2. Fix the type parameter order: register.Combiner2[AccType, InputType] — accumulator type goes first.
  3. Remove parameters from CreateAccumulator; move initialization/seed data into struct fields set at construction.
  4. Register with a pointer (&MyCombiner{}) if methods use pointer receivers, and match named types exactly in the generic argument.
  5. If the signature can't conform, use the non-optimized registration path instead of Combiner1/2/3.

Example fix

// before: CreateAccumulator returns wrong type vs type param
type CountFn struct{}
func (fn *CountFn) CreateAccumulator() int { return 0 }
register.Combiner1[Count](...) // Count is `type Count int`

// after: align types
type CountFn struct{}
func (fn *CountFn) CreateAccumulator() Count { return Count(0) }
register.Combiner1[Count](&CountFn{})
Defensive patterns

Strategy: type-guard

Validate before calling

// Verify CreateAccumulator shape before CombinerN registration:
func createIsOptimizable[T any](accum any) bool {
    _, ok1 := accum.(interface{ CreateAccumulator() (T, error) })
    _, ok2 := accum.(interface{ CreateAccumulator() T })
    return ok1 || ok2
}

Type guard

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

Try / catch

func registerCombinerSafe[T any](accum any) {
    defer func() {
        if r := recover(); r != nil {
            log.Fatalf("CreateAccumulator on %T doesn't match func() T or func() (T, error) for T=%T: %v", accum, *new(T), r)
        }
    }()
    register.Combiner1[T](accum)
}

Prevention

When it happens

Trigger: register.CombinerN[T](&MyCombiner{}) where MyCombiner.CreateAccumulator returns something other than T0 or (T0, error) — e.g. returns a different type than the supplied type parameter, takes parameters (CreateAccumulator(seed T)), returns multiple non-error values, or the accumulator type argument simply doesn't match the method's return type.

Common situations: Wrong generic argument order in Combiner2/Combiner3 (accumulator must be first); CreateAccumulator initialized with config/seed parameters; combining named types vs the primitive type argument (type int vs type Count int); pointer vs value receiver making the interface assertion fail.

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/b492a495561cbabe. Report an issue: GitHub.