{"record":{"id":"63e38b8f0729cc40","repo":"apache/beam","slug":"bad-function","errorCode":null,"errorMessage":"bad function","messagePattern":"bad function","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/runtime/graphx/serialize.go","lineNumber":105,"sourceCode":"\treturn ret, nil\n}\n\n// DecodeMultiEdge converts the wire representation into the preprocessed\n// components representing that edge. We deserialize to components to avoid\n// inserting the edge into a graph or creating a detached edge.\nfunc DecodeMultiEdge(edge *v1pb.MultiEdge) (graph.Opcode, *graph.Fn, *window.Fn, []*graph.Inbound, []*graph.Outbound, error) {\n\tvar u *graph.Fn\n\tvar wfn *window.Fn\n\tvar inbound []*graph.Inbound\n\tvar outbound []*graph.Outbound\n\n\topcode := graph.Opcode(edge.Opcode)\n\n\tif edge.Fn != nil {\n\t\tvar err error\n\t\tu, err = decodeFn(edge.Fn)\n\t\tif err != nil {\n\t\t\twrapped := errors.Wrap(err, \"bad function\")\n\t\t\treturn \"\", nil, nil, nil, nil, errors.WithContextf(wrapped, \"decoding userfn %v\", edge)\n\t\t}\n\t}\n\tif edge.WindowFn != nil {\n\t\twfn = decodeWindowFn(edge.WindowFn)\n\t}\n\tfor _, in := range edge.Inbound {\n\t\tkind, err := decodeInputKind(in.Kind)\n\t\tif err != nil {\n\t\t\twrapped := errors.Wrap(err, \"bad input kind\")\n\t\t\treturn \"\", nil, nil, nil, nil, errors.WithContextf(wrapped, \"decoding userfn %v\", edge)\n\t\t}\n\t\tt, err := decodeFullType(in.Type)\n\t\tif err != nil {\n\t\t\twrapped := errors.Wrap(err, \"bad input type\")\n\t\t\treturn \"\", nil, nil, nil, nil, errors.WithContextf(wrapped, \"decoding userfn %v\", edge)\n\t\t}\n\t\tinbound = append(inbound, &graph.Inbound{Kind: kind, Type: t})","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/runtime/graphx/serialize.go#L87-L123","documentation":"DecodeMultiEdge wraps decodeFn failures as 'bad function' (context 'decoding userfn'). When deserializing a graph, the encoded Fn reference (payload + config) must resolve back into a DoFn/CombineFn; failures include unresolvable function paths, missing types, or payload decode errors on the receiving (often remote worker) side.","triggerScenarios":"DecodeMultiEdge called from makeLink on an edge with edge.Fn != nil where decodeFn fails: the fn's package path/type cannot be resolved (code not on the worker), the serialized payload cannot be unmarshaled, or registered types are missing in the decoding process.","commonSituations":"Remote execution where the worker binary is stale and lacks the pipeline's DoFn types; package refactor moving functions so recorded paths no longer resolve; version mismatch between submitting SDK and worker; unregistered custom config types.","solutions":["Read the wrapped decodeFn error: path-resolution vs payload-decode tells you which side is stale","Rebuild/redeploy the worker with the latest user code so all fn types resolve","Rebuild the pipeline and resubmit after any package refactor (avoid renaming/moving DoFns between submit and run)","Register all custom types used in fn payloads so payload unmarshaling succeeds"],"exampleFix":"// before\n// worker binary built before adding type myFn; graph references myFn -> bad function\n// after\ngo build -o worker ./cmd/worker // rebuild worker with current user code, then resubmit","handlingStrategy":"try-catch","validationCode":"func canDecodeFn(ref *pipepb.FunctionRef) error {\n  fnT, err := typeForPath(ref.GetFn().GetPath())\n  if err != nil { return fmt.Errorf(\"fn path %q unresolved: %w\", ref.GetFn().GetPath(), err) }\n  return validatePayloadTypes(ref, fnT)\n}","typeGuard":"func fnResolvable(path string) bool {\n  _, err := typeForPath(path)\n  return err == nil\n}","tryCatchPattern":"u, err := decodeFn(edge.Fn)\nif err != nil {\n  return fmt.Errorf(\"decoding userfn %v: fn %q missing or payload invalid: %w\", edge, edge.Fn.GetFn().GetPath(), err)\n}","preventionTips":["Rebuild worker binaries whenever user code changes","Avoid renaming/moving DoFn packages between graph construction and execution","Keep submit-side and worker-side Beam versions identical","Register all custom payload types in worker init()"],"tags":["go","beam","serialization","deserialization"],"backgroundTag":"protobuf-unmarshal-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"}