{"record":{"id":"6b725ef2a13643c8","repo":"apache/beam","slug":"failed-to-optimize-extractoutput-for-combiner-v-failed-to","errorCode":null,"errorMessage":"Failed to optimize ExtractOutput for combiner %v. Failed to infer types","messagePattern":"Failed to optimize ExtractOutput for combiner (.+?)\\. Failed to infer types","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/register/register.go","lineNumber":7986,"sourceCode":"\t\t\t\treturn fn.(extractOutput1x2[T0, T0]).ExtractOutput(a0)\n\t\t\t})\n\t\t}\n\t} else if _, ok := accum.(extractOutput1x1[T0, T0]); ok {\n\t\tcaller := func(fn any) reflectx.Func {\n\t\t\tf := fn.(func(T0) T0)\n\t\t\treturn &caller1x1[T0, T0]{fn: f}\n\t\t}\n\t\treflectx.RegisterFunc(reflect.TypeOf((*func(T0) T0)(nil)).Elem(), caller)\n\n\t\textractOutputWrapper = func(fn any) reflectx.Func {\n\t\t\treturn reflectx.MakeFunc(func(a0 T0) T0 {\n\t\t\t\treturn fn.(extractOutput1x1[T0, T0]).ExtractOutput(a0)\n\t\t\t})\n\t\t}\n\t}\n\n\tif m := accumVal.MethodByName(\"ExtractOutput\"); m.IsValid() && extractOutputWrapper == nil {\n\t\tpanic(fmt.Sprintf(\"Failed to optimize ExtractOutput for combiner %v. Failed to infer types\", accum))\n\t}\n\n\twrapperFn := func(fn any) map[string]reflectx.Func {\n\t\tm := map[string]reflectx.Func{}\n\t\tif mergeAccumulatorsWrapper != nil {\n\t\t\tm[\"MergeAccumulators\"] = mergeAccumulatorsWrapper(fn)\n\t\t}\n\t\tif createAccumulatorWrapper != nil {\n\t\t\tm[\"CreateAccumulator\"] = createAccumulatorWrapper(fn)\n\t\t}\n\t\tif addInputWrapper != nil {\n\t\t\tm[\"AddInput\"] = addInputWrapper(fn)\n\t\t}\n\t\tif extractOutputWrapper != nil {\n\t\t\tm[\"ExtractOutput\"] = extractOutputWrapper(fn)\n\t\t}\n\n\t\treturn m","sourceCodeStart":7968,"sourceCodeEnd":8004,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/register/register.go#L7968-L8004","documentation":"Panicked from register.Combiner1/Combiner2 in register.go. The registration code tries to wrap ExtractOutput via extractOutput1x2[T0,T0] (ExtractOutput(T0) (T0, error)) or extractOutput1x1[T0,T0] (ExtractOutput(T0) T0). If the combiner exposes an ExtractOutput method (detected by MethodByName) that fits neither shape for the inferred types, no wrapper can be constructed and the library panics.","triggerScenarios":"register.Combiner1[T](&c{}) or Combiner2 where c.ExtractOutput's parameter or return type doesn't equal the inferred T0 — e.g. ExtractOutput(accum AccumT) OutputT with AccumT != OutputT under Combiner1, extra arguments, or returns types unsupported by the wrappers.","commonSituations":"Combiner producing a different output type than the accumulator (e.g. averaging accumulates [sum,count] and extracts a float) registered with Combiner1 instead of Combiner2; type parameter order mistakes in Combiner2; refactoring ExtractOutput to return multiple values.","solutions":["Make ExtractOutput match `func (c *C) ExtractOutput(accum T) T` or `func (c *C) ExtractOutput(accum T) (T, error)` for the type parameter used.","If output type differs from accumulator type, register with Combiner2[AccumT, OutputT] and shape methods accordingly.","Ensure ExtractOutput takes exactly one accumulator argument.","Remove ExtractOutput if the accumulator itself is the output."],"exampleFix":"// before\nfunc (c *Sum) ExtractOutput(a *Stats) int { return a.total }\nregister.Combiner1[*Stats](&Sum{})\n\n// after\nfunc (c *Sum) ExtractOutput(a *Stats) (*Stats, error) { return a, nil }\nregister.Combiner1[*Stats](&Sum{})","handlingStrategy":"validation","validationCode":"func validateExtractOutput[T0 any](c any) bool {\n\t_, ok1 := c.(interface{ ExtractOutput(T0) T0 })\n\t_, ok2 := c.(interface{ ExtractOutput(T0) (T0, error) })\n\treturn ok1 || ok2\n}\n// verify before register.Combiner1[T0](&c{})","typeGuard":"func hasTypedExtractOutput[T0 any](c any) bool {\n\t_, ok1 := c.(interface{ ExtractOutput(T0) T0 })\n\t_, ok2 := c.(interface{ ExtractOutput(T0) (T0, error) })\n\treturn ok1 || ok2\n}","tryCatchPattern":null,"preventionTips":["Make ExtractOutput take one accumulator and return that same type (plus optional error).","Use Combiner2 when the extracted output type differs from the accumulator type.","Avoid returning multiple values from ExtractOutput beyond (T, error).","Exercise the register call in tests so the panic appears at CI time."],"tags":["go","apache-beam","combiner","signature-mismatch","panic"],"backgroundTag":"type-mismatch","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}