{"record":{"id":"5c539662ff042583","repo":"apache/beam","slug":"bad-combinefn","errorCode":null,"errorMessage":"bad combinefn","messagePattern":"bad combinefn","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/runtime/graphx/serialize.go","lineNumber":57,"sourceCode":"\n// EncodeMultiEdge converts the preprocessed representation into the wire\n// representation of the multiedge, capturing input and output type information.\nfunc EncodeMultiEdge(edge *graph.MultiEdge) (*v1pb.MultiEdge, error) {\n\tret := &v1pb.MultiEdge{}\n\tret.Opcode = string(edge.Op)\n\n\tif edge.DoFn != nil {\n\t\tref, err := encodeFn((*graph.Fn)(edge.DoFn))\n\t\tif err != nil {\n\t\t\twrapped := errors.Wrap(err, \"bad userfn\")\n\t\t\treturn nil, errors.WithContextf(wrapped, \"encoding userfn %v\", edge)\n\t\t}\n\t\tret.Fn = ref\n\t}\n\tif edge.CombineFn != nil {\n\t\tref, err := encodeFn((*graph.Fn)(edge.CombineFn))\n\t\tif err != nil {\n\t\t\twrapped := errors.Wrap(err, \"bad combinefn\")\n\t\t\treturn nil, errors.WithContextf(wrapped, \"encoding userfn %v\", edge)\n\t\t}\n\t\tret.Fn = ref\n\t}\n\tif edge.WindowFn != nil {\n\t\tret.WindowFn = encodeWindowFn(edge.WindowFn)\n\t}\n\n\tfor _, in := range edge.Input {\n\t\tkind, err := encodeInputKind(in.Kind)\n\t\tif err != nil {\n\t\t\twrapped := errors.Wrap(err, \"bad input type\")\n\t\t\treturn nil, errors.WithContextf(wrapped, \"encoding userfn %v\", edge)\n\t\t}\n\t\tt, err := encodeFullType(in.Type)\n\t\tif err != nil {\n\t\t\twrapped := errors.Wrap(err, \"bad input type\")\n\t\t\treturn nil, errors.WithContextf(wrapped, \"encoding userfn %v\", edge)","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/runtime/graphx/serialize.go#L39-L75","documentation":"EncodeMultiEdge wraps encodeFn failures for a CombineFn as 'bad combinefn' (also context-tagged 'encoding userfn'). Like DoFns, CombineFns must be serializable named types with resolvable references. The error indicates the graph encoder could not turn the CombineFn into its protobuf representation.","triggerScenarios":"EncodeMultiEdge on an edge with edge.CombineFn != nil where encodeFn fails — closures, anonymous structs, unregistered custom accumulator types, or encode-time type resolution failures.","commonSituations":"beam.Combine with an inline/anonymous combine function; custom accumulator types lacking registration when shipping to a remote runner; refactoring that breaks recorded function paths.","solutions":["Check the wrapped inner error from encodeFn for the root cause","Define the CombineFn as a named exported top-level type","Register accumulator/result types used by the CombineFn","Re-run pipeline construction after fixing the fn definition"],"exampleFix":"// before\nbeam.Combine(s, func(a, b int) int { return a + b }, in) // closure\n// after\ntype sumFn struct{}\nfunc (sumFn) CreateAccumulator() int { return 0 }\nfunc (sumFn) AddInput(a, b int) int { return a + b }\nfunc (sumFn) MergeAccumulators(a, b int) int { return a + b }\nbeam.Combine(s, sumFn{}, in)","handlingStrategy":"validation","validationCode":"if !isSerializableCombineFn(edge.CombineFn) {\n  return errors.New(\"CombineFn must be a named exported type with registered accumulator types\")\n}","typeGuard":"func isSerializableCombineFn(fn interface{}) bool {\n  t := reflect.TypeOf(fn)\n  return t != nil && t.Name() != \"\" && t.PkgPath() != \"\"\n}","tryCatchPattern":"ref, err := encodeFn((*graph.Fn)(edge.CombineFn))\nif err != nil {\n  return fmt.Errorf(\"CombineFn %T not serializable: %w\", edge.CombineFn, err)\n}","preventionTips":["Define CombineFns as named top-level types","Register accumulator/result types for remote marshaling","Avoid inline function literals passed to beam.Combine","Smoke-test graph encoding before submitting to a runner"],"tags":["go","beam","serialization","combinefn"],"backgroundTag":"json-marshal-failed","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"}