apache/beam · error
structural DoFn passed by value, ensure that the…
Error message
structural DoFn passed by value, ensure that the ProcessElement method has a value receiver or pass the DoFn by pointer
What it means
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.
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
Example fix
// before func (m MyFn) is fine — but if receiver is *MyFn: beam.ParDo(s, m, in) // after beam.ParDo(s, &m, in)
Defensive patterns
Strategy: validation
Validate before calling
if reflect.TypeOf(fn).Kind() == reflect.Ptr { _, ok := reflect.TypeOf(fn).MethodByName("ProcessElement"); if !ok { return errors.New("ProcessElement not reachable") } } Type guard
func isStructPointer(fn any) bool {
v := reflect.ValueOf(fn)
return v.IsValid() && v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct
} Prevention
- Convention: always construct DoFns as &MyFn{}
- Prefer value receivers on ProcessElement if the DoFn is stateless
When it happens
Trigger: 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.
Common situations: Copy-pasting a DoFn and forgetting the & when passing it to beam.ParDo; passing DoFn values stored in variables of value type.
Related errors
- Duplicate state key used by and . Ensure that state keys…
- emit parameter in method
- emit parameters expected in method
- error writing state
- failed to find method
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/0fa67cb4b77ff7ec.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/core/graph/fn.go:507
// validation is done by best effort and may miss some edge cases.
func AsDoFn(fn *Fn, numMainIn mainInputs) (*DoFn, error) {
addContext := func(err error, fn *Fn) error {
return errors.WithContextf(err, "graph.AsDoFn: for Fn named %v", fn.Name())
}
if fn.methods == nil {
fn.methods = make(map[string]*funcx.Fn)
}
if fn.Fn != nil {
fn.methods[processElementName] = fn.Fn
}
if _, ok := fn.methods[processElementName]; !ok {
err := errors.Errorf("failed to find %v method", processElementName)
if fn.Recv != nil {
v := reflect.ValueOf(fn.Recv)
if v.Kind() != reflect.Ptr {
err = errors.Wrap(err, "structural DoFn passed by value, ensure that the ProcessElement method has a value receiver or pass the DoFn by pointer")
}
}
return nil, addContext(err, fn)
}
// Make sure that all state entries have keys. If they don't set them to the struct field name.
if fn.Recv != nil {
v := reflect.Indirect(reflect.ValueOf(fn.Recv))
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
if f.CanInterface() {
if ps, ok := f.Interface().(state.PipelineState); ok {
if ps.StateKey() == "" {
f.FieldByName("Key").SetString(v.Type().Field(i).Name)
}
}
}
}View on GitHub (pinned to 12126d8942)