{"record":{"id":"09ea0549eb1d2488","repo":"apache/beam","slug":"bad-decoding-function","errorCode":null,"errorMessage":"bad decoding function","messagePattern":"bad decoding function","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/runtime/graphx/serialize.go","lineNumber":149,"sourceCode":"\t\toutbound = append(outbound, &graph.Outbound{Type: t})\n\t}\n\n\treturn opcode, u, wfn, inbound, outbound, nil\n}\n\nfunc encodeCustomCoder(c *coder.CustomCoder) (*v1pb.CustomCoder, error) {\n\tt, err := encodeType(c.Type)\n\tif err != nil {\n\t\treturn nil, errors.WithContextf(err, \"encoding custom coder %v for type %v\", c, c.Type)\n\t}\n\tenc, err := encodeUserFn(c.Enc)\n\tif err != nil {\n\t\twrapped := errors.Wrap(err, \"bad encoding function\")\n\t\treturn nil, errors.WithContextf(wrapped, \"encoding custom coder %v\", c)\n\t}\n\tdec, err := encodeUserFn(c.Dec)\n\tif err != nil {\n\t\twrapped := errors.Wrap(err, \"bad decoding function\")\n\t\treturn nil, errors.WithContextf(wrapped, \"encoding custom coder %v\", c)\n\t}\n\n\tret := &v1pb.CustomCoder{\n\t\tName: c.Name,\n\t\tType: t,\n\t\tEnc:  enc,\n\t\tDec:  dec,\n\t}\n\treturn ret, nil\n}\n\nfunc decodeCustomCoder(c *v1pb.CustomCoder) (*coder.CustomCoder, error) {\n\tt, err := decodeType(c.Type)\n\tif err != nil {\n\t\treturn nil, errors.WithContextf(err, \"decoding custom coder %v for type %v\", c, c.Type)\n\t}\n\tenc, err := decodeUserFn(c.Enc)","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/runtime/graphx/serialize.go#L131-L167","documentation":"encodeCustomCoder calls encodeUserFn on the decoding function c.Dec; if that function cannot be resolved to a serializable named function reference, the error is wrapped as \"bad decoding function\". Like its encoding counterpart, Beam can only serialize coders whose functions are named, top-level, addressable functions.","triggerScenarios":"coder.NewCustomCoder created with an anonymous or closure decode function (or a bound method), then serialized through Add or EncodeCoderRef — encodeUserFn(c.Dec) fails.","commonSituations":"Inline func literals as decode callbacks; decoder functions defined inside main or tests where the decoder binary can't resolve them; refactor moved the function so the recorded name no longer resolves.","solutions":["Make the decode function a named package-level function","Replace bound method values with package-level wrapper functions","Ensure the package defining the decoder is importable by the decoding worker","Test the coder round-trip locally with EncodeCoderRef/DecodeCoderRef"],"exampleFix":"// before\ndec := func(b []byte) (T, error) { ... }\nc, _ := coder.NewCustomCoder(\"myCoder\", typ, enc, dec)\n// after\nfunc decodeT(b []byte) (T, error) { ... }\nc, _ := coder.NewCustomCoder(\"myCoder\", typ, encodeT, decodeT)","handlingStrategy":"type-guard","validationCode":"func isNamedFunc(f interface{}) bool {\n\tname := reflectx.FunctionName(f)\n\treturn name != \"\" && !strings.Contains(name, \"func\") && !strings.Contains(name, \"-\")\n}\n// validate: isNamedCoder(c.Dec)","typeGuard":"func isNamedFunc(f interface{}) bool {\n\tname := reflectx.FunctionName(f)\n\treturn name != \"\" && !strings.Contains(name, \"func\") && !strings.Contains(name, \"-\")\n}","tryCatchPattern":"ref, err := graphx.EncodeCoderRef(c)\nif err != nil && strings.Contains(err.Error(), \"bad decoding function\") {\n\treturn fmt.Errorf(\"coder %v uses non-serializable dec fn: %w\", c, err)\n}","preventionTips":["Use named top-level functions for coder Dec","Avoid closures/method values in coders","Test EncodeCoderRef on all custom coders in CI","Keep coder packages importable by workers"],"tags":["go","beam","coder","serialization"],"backgroundTag":"unsupported-function-value","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"}