{"record":{"id":"ce136f6a71d1105e","repo":"apache/beam","slug":"value-v-must-be-function-or-ptr-to-struct","errorCode":null,"errorMessage":"value %v must be function or (ptr to) struct","messagePattern":"value (.+?) must be function or \\(ptr to\\) struct","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/graph/fn.go","lineNumber":149,"sourceCode":"\t\t\t}\n\t\t\tm, ok := val.Type().MethodByName(mName)\n\t\t\tif !ok {\n\t\t\t\tcontinue // skip: doesn't exist\n\t\t\t}\n\n\t\t\t// CAVEAT(herohde) 5/22/2017: The type val.Type.Method.Type is not\n\t\t\t// the same as val.Method.Type: the former has the explicit receiver.\n\t\t\t// We'll use the receiver-less version.\n\t\t\tf, err := funcx.New(reflectx.MakeFunc(val.Method(m.Index).Interface()))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, errors.Wrapf(err, \"method %v invalid\", mName)\n\t\t\t}\n\t\t\tmethods[mName] = f\n\t\t}\n\t\treturn &Fn{Recv: fn, methods: methods, annotations: annotations}, nil\n\n\tdefault:\n\t\treturn nil, errors.Errorf(\"value %v must be function or (ptr to) struct\", fn)\n\t}\n}\n\n// Signature method names.\nconst (\n\tsetupName          = \"Setup\"\n\tstartBundleName    = \"StartBundle\"\n\tprocessElementName = \"ProcessElement\"\n\tfinishBundleName   = \"FinishBundle\"\n\tteardownName       = \"Teardown\"\n\n\tcreateInitialRestrictionName = \"CreateInitialRestriction\"\n\tsplitRestrictionName         = \"SplitRestriction\"\n\trestrictionSizeName          = \"RestrictionSize\"\n\tcreateTrackerName            = \"CreateTracker\"\n\ttruncateRestrictionName      = \"TruncateRestriction\"\n\n\tcreateWatermarkEstimatorName       = \"CreateWatermarkEstimator\"","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/graph/fn.go#L131-L167","documentation":"Error returned by NewFn when the value passed to be treated as a DoFn/CombineFn is of an unsupported kind — not a function and not a struct or pointer-to-struct with the expected methods. The default branch of the constructor, it names the actual type, telling the user to pass a func or a (pointer to) struct implementing the DoFn/CombineFn method set.","triggerScenarios":"Calling NewFn, NewDoFn, or NewCombineFn with a value that is neither a function nor *struct, e.g. passing a struct by value or an int/string placeholder.","commonSituations":"Passing a struct literal (not &struct) to beam.ParDo; using a variable whose type changed from func to struct; accidentally passing nil or an interface holding the wrong kind.","solutions":["Pass a function or a pointer to a struct: beam.ParDo(s, &myFn{}, in)","If using a structural DoFn, take its address before passing","Add a compile-time assertion like var _ = fn (func or *struct) in tests"],"exampleFix":"// before\nbeam.ParDo(s, MyFn{count: 3}, in)\n// after\nbeam.ParDo(s, &MyFn{count: 3}, in)","handlingStrategy":"validation","validationCode":"switch v := fn.(type) {\ncase func(...), *struct{}:\n    // ok\ndefault:\n    return fmt.Errorf(\"fn %T must be func or *struct\", fn)\n}","typeGuard":"func isFnValue(fn any) bool { return reflect.ValueOf(fn).Kind() == reflect.Func || (reflect.ValueOf(fn).Kind() == reflect.Ptr && reflect.ValueOf(fn).Elem().Kind() == reflect.Struct) }","tryCatchPattern":null,"preventionTips":["Always pass DoFns as &MyFn{} pointers","Never pass struct values or non-fn primitives to beam.ParDo/beam.Combine"],"tags":["go","apache-beam","dofn"],"backgroundTag":"invalid-argument-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"}