{"record":{"id":"998af3e1eb0c9d42","repo":"apache/beam","slug":"bad-struct-encoding","errorCode":null,"errorMessage":"bad struct encoding","messagePattern":"bad struct encoding","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/runtime/graphx/serialize.go","lineNumber":310,"sourceCode":"\t\t\twrapped := errors.Wrap(err, \"failed to decode userfn\")\n\t\t\treturn nil, errors.WithContextf(wrapped, \"decoding DoFn %v\", u)\n\t\t}\n\t\tfx, err := funcx.New(reflectx.MakeFunc(fn))\n\t\tif err != nil {\n\t\t\twrapped := errors.Wrap(err, \"failed to construct userfn\")\n\t\t\treturn nil, errors.WithContextf(wrapped, \"decoding DoFn %v\", u)\n\t\t}\n\t\treturn &graph.Fn{Fn: fx}, nil\n\t}\n\n\tt, err := decodeType(u.Type)\n\tif err != nil {\n\t\twrapped := errors.Wrap(err, \"bad type\")\n\t\treturn nil, errors.WithContextf(wrapped, \"decoding structural DoFn %v\", u)\n\t}\n\telem := reflect.New(t)\n\tif err := jsonx.UnmarshalFrom(elem.Interface(), strings.NewReader(u.Opt)); err != nil {\n\t\twrapped := errors.Wrap(err, \"bad struct encoding\")\n\t\treturn nil, errors.WithContextf(wrapped, \"decoding structural DoFn %v\", u)\n\t}\n\tfn := elem.Elem().Interface()\n\treturn graph.NewFn(fn)\n}\n\n// encodeUserFn translates the preprocessed representation of a Beam user function\n// into the wire representation, capturing all the inputs and outputs needed.\nfunc encodeUserFn(u *funcx.Fn) (*v1pb.UserFn, error) {\n\t// TODO(herohde) 5/23/2017: reject closures and dynamic functions. They can't\n\t// be serialized.\n\n\tsymbol := u.Fn.Name()\n\tt, err := encodeType(u.Fn.Type())\n\tif err != nil {\n\t\twrapped := errors.Wrap(err, \"bad function type\")\n\t\treturn nil, errors.WithContextf(wrapped, \"encoding userfn %v\", u)\n\t}","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/runtime/graphx/serialize.go#L292-L328","documentation":"After decoding the structural DoFn's type, decodeFn unmarshals the JSON Opt payload into a new value of that type with jsonx.UnmarshalFrom; failure is wrapped as \"bad struct encoding\". The serialized DoFn state cannot be applied to the reconstructed struct.","triggerScenarios":"DecodeMultiEdge decoding a structural DoFn whose u.Opt JSON no longer matches the struct's fields/types in the worker's version of the type.","commonSituations":"DoFn struct fields renamed, retyped, or removed after the pipeline was serialized; custom UnmarshalJSON methods failing; JSON-incompatible fields added to the struct.","solutions":["Keep the DoFn struct's JSON-visible shape stable between pipeline submission and execution.","Check the wrapped jsonx error for the offending field and fix the mismatch (name or type).","Add `json:\"-\"` to fields not meant to travel, and reinitialize them in Setup().","Re-submit the pipeline after any change to DoFn struct fields."],"exampleFix":"// before\ntype myFn struct{ Count int64 } // was Count int before rename on one side\n\n// after\ntype myFn struct {\n    Count int64 `json:\"count\"` // stable JSON key across versions\n}","handlingStrategy":"validation","validationCode":"if err := json.Unmarshal([]byte(optJSON), reflect.New(reflect.TypeOf(myFn{})).Interface()); err != nil {\n    return fmt.Errorf(\"DoFn state JSON incompatible with current struct: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := jsonx.UnmarshalFrom(elem.Interface(), strings.NewReader(u.Opt)); err != nil {\n    return fmt.Errorf(\"structural DoFn state mismatch; resubmit the pipeline with the current struct definition: %w\", err)\n}","preventionTips":["Treat DoFn struct fields as a serialization contract: add fields with omitempty, never rename or retype.","Tag fields explicitly with stable JSON names.","Re-submit pipelines whenever DoFn struct definitions change."],"tags":["go","json","deserialization","apache-beam"],"backgroundTag":"json-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"}