{"record":{"id":"5df665a17fa9cab6","repo":"apache/beam","slug":"incompatible-func-type-got-func-v-with-v-inputs-and-v-5df665","errorCode":null,"errorMessage":"Incompatible func type: got func %v with %v inputs and %v outputs, want {{$in}} inputs and {{$out}} outputs","messagePattern":"Incompatible func type: got func (.+?) with (.+?) inputs and (.+?) outputs, want (.+?)\\} inputs and (.+?)\\} outputs","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/util/reflectx/calls.tmpl","lineNumber":65,"sourceCode":"}\n\nfunc (c *shimFunc{{$in}}x{{$out}}) Type() reflect.Type {\n   return c.inner.Type()\n}\n\nfunc (c *shimFunc{{$in}}x{{$out}}) Call(args []any) []any {\n   return c.inner.Call(args)\n}\n\nfunc (c *shimFunc{{$in}}x{{$out}}) Call{{$in}}x{{$out}}({{mkargs $in \"arg%v\" \"any\"}}) ({{mktuple $out \"any\"}}) {\n   ret := c.inner.Call([]any{ {{mktuplef $in \"arg%v\"}} })\n   _ = ret\n   return {{mktuplef $out \"ret[%v]\"}}\n}\n\nfunc ToFunc{{$in}}x{{$out}}(c Func) Func{{$in}}x{{$out}} {\n    if c.Type().NumIn() != {{$in}} || c.Type().NumOut() != {{$out}} {\n        panic(fmt.Sprintf(\"Incompatible func type: got func %v with %v inputs and %v outputs, want {{$in}} inputs and {{$out}} outputs\", c.Type(), c.Type().NumIn(), c.Type().NumOut()))\n    }\n    if sc, ok := c.(Func{{$in}}x{{$out}}); ok {\n        return sc\n    }\n    return &shimFunc{{$in}}x{{$out}}{inner: c}\n}\n\nfunc MakeFunc{{$in}}x{{$out}}(fn any) Func{{$in}}x{{$out}} {\n    return ToFunc{{$in}}x{{$out}}(MakeFunc(fn))\n}\n{{end}}\n{{end}}\n","sourceCodeStart":47,"sourceCodeEnd":78,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/util/reflectx/calls.tmpl#L47-L78","documentation":"reflectx's generated ToFunc{In}x{Out} converters wrap a generic Func as a strongly-typed Func{In}x{Out} shim. Before wrapping, it verifies the function's reflect type has exactly the expected number of inputs and outputs; a mismatch panics because the shim's call ABI would otherwise be violated. This is a template-time arity guard used by Beam's core function utilities.","triggerScenarios":"Calling a ToFuncNxM converter (e.g., ToFunc1x2) with a Func whose reflect.Type has a different NumIn/NumOut than N/M.","commonSituations":"Wrapping user callbacks during pipeline graph construction with the wrong arity converter; signature changes in user DoFn/combine functions after the converter call site was written.","solutions":["Use the ToFunc variant matching the function's actual input and output counts (e.g., ToFunc2x1 for a 2-in/1-out func)","Check c.Type().NumIn() and NumOut() (accounting for receiver vs closure differences) before choosing the converter","Refactor the target function to the expected arity instead of forcing the wrong converter"],"exampleFix":"// before: function has 2 inputs but wrapped as 1x\nf := reflectx.ToFunc1x1(myTwoInputFunc)\n\n// after\nf := reflectx.ToFunc2x1(myTwoInputFunc)","handlingStrategy":"type-guard","validationCode":"if c.Type().NumIn() != wantIn || c.Type().NumOut() != wantOut {\n    return fmt.Errorf(\"func has %d in / %d out, need %d in / %d out\", c.Type().NumIn(), c.Type().NumOut(), wantIn, wantOut)\n}","typeGuard":"func matchesArity(c reflectx.Func, in, out int) bool {\n    return c.Type().NumIn() == in && c.Type().NumOut() == out\n}","tryCatchPattern":"defer func() {\n    if r := recover(); strings.Contains(fmt.Sprint(r), \"Incompatible func type\") {\n        log.Fatalf(\"reflectx converter arity mismatch: %v\", r)\n    }\n}()","preventionTips":["Match the ToFuncNxM variant to the function's actual signature","Check NumIn/NumOut before wrapping callbacks","Avoid changing function signatures without updating converter call sites"],"tags":["go","apache-beam","reflection","arity","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-20T03:17:13.778Z"}