{"record":{"id":"0fa67cb4b77ff7ec","repo":"apache/beam","slug":"structural-dofn-passed-by-value-ensure-that-the","errorCode":null,"errorMessage":"structural DoFn passed by value, ensure that the ProcessElement method has a value receiver or pass the DoFn by pointer","messagePattern":"structural DoFn passed by value, ensure that the ProcessElement method has a value receiver or pass the DoFn by pointer","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/graph/fn.go","lineNumber":507,"sourceCode":"// validation is done by best effort and may miss some edge cases.\nfunc AsDoFn(fn *Fn, numMainIn mainInputs) (*DoFn, error) {\n\taddContext := func(err error, fn *Fn) error {\n\t\treturn errors.WithContextf(err, \"graph.AsDoFn: for Fn named %v\", fn.Name())\n\t}\n\n\tif fn.methods == nil {\n\t\tfn.methods = make(map[string]*funcx.Fn)\n\t}\n\tif fn.Fn != nil {\n\t\tfn.methods[processElementName] = fn.Fn\n\t}\n\n\tif _, ok := fn.methods[processElementName]; !ok {\n\t\terr := errors.Errorf(\"failed to find %v method\", processElementName)\n\t\tif fn.Recv != nil {\n\t\t\tv := reflect.ValueOf(fn.Recv)\n\t\t\tif v.Kind() != reflect.Ptr {\n\t\t\t\terr = errors.Wrap(err, \"structural DoFn passed by value, ensure that the ProcessElement method has a value receiver or pass the DoFn by pointer\")\n\t\t\t}\n\t\t}\n\t\treturn nil, addContext(err, fn)\n\t}\n\n\t// Make sure that all state entries have keys. If they don't set them to the struct field name.\n\tif fn.Recv != nil {\n\t\tv := reflect.Indirect(reflect.ValueOf(fn.Recv))\n\t\tfor i := 0; i < v.NumField(); i++ {\n\t\t\tf := v.Field(i)\n\t\t\tif f.CanInterface() {\n\t\t\t\tif ps, ok := f.Interface().(state.PipelineState); ok {\n\t\t\t\t\tif ps.StateKey() == \"\" {\n\t\t\t\t\t\tf.FieldByName(\"Key\").SetString(v.Type().Field(i).Name)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}","sourceCodeStart":489,"sourceCodeEnd":525,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/graph/fn.go#L489-L525","documentation":"A secondary hint wrapped onto the \"failed to find ProcessElement method\" error when fn.Recv is non-nil and is not a pointer. It tells the developer that Go reflection on a value receiver cannot surface pointer-receiver methods.","triggerScenarios":"AsDoFn on a struct value (not pointer) where ProcessElement was declared with a pointer receiver, so the method set of the value does not include it.","commonSituations":"Copy-pasting a DoFn and forgetting the & when passing it to beam.ParDo; passing DoFn values stored in variables of value type.","solutions":["Pass the DoFn by pointer (add &)","Or switch ProcessElement to a value receiver so it is in the value's method set","Store DoFn instances as pointer-typed variables to avoid recurrence"],"exampleFix":"// before\nfunc (m MyFn) is fine — but if receiver is *MyFn: beam.ParDo(s, m, in)\n// after\nbeam.ParDo(s, &m, in)","handlingStrategy":"validation","validationCode":"if reflect.TypeOf(fn).Kind() == reflect.Ptr { _, ok := reflect.TypeOf(fn).MethodByName(\"ProcessElement\"); if !ok { return errors.New(\"ProcessElement not reachable\") } }","typeGuard":"func isStructPointer(fn any) bool {\n  v := reflect.ValueOf(fn)\n  return v.IsValid() && v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct\n}","tryCatchPattern":null,"preventionTips":["Convention: always construct DoFns as &MyFn{}","Prefer value receivers on ProcessElement if the DoFn is stateless"],"tags":["go","apache-beam","dofn","method-set"],"backgroundTag":"method-not-implemented","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"}