apache/beam · error
value must be ptr to struct
Error message
value %v must be ptr to struct
What it means
NewFn in fn.go accepts function values, function pointers, pointers to structs (DoFn/CombineFn receivers), and interfaces. When the value is a reflect.Ptr but the pointed-to value is not a struct, it throws "value %v must be ptr to struct". Struct-pointer receivers are required so user code can mutate DoFn state across elements; pointers to non-structs have no valid lifecycle methods to bind.
Solutions
- Pass the function value itself (e.g., `myFunc`) or a pointer to a struct implementing the DoFn/CombineFn methods.
- If using a struct-based DoFn, define ProcessElement (and lifecycle) methods on it and pass `&myDoFn{...}`.
- If passing a func, remove the `&` — pointers to funcs are not valid fn values.
- Check the value's type with reflect before calling the beam API to fail fast with a clearer message.
Example fix
// before
n := 0
beam.ParDo(s, &n, coll) // ptr to non-struct
// after
type adder struct{ n int }
func (a *adder) ProcessElement(x int) int { return x + a.n }
beam.ParDo(s, &adder{n: 0}, coll) Defensive patterns
Strategy: type-guard
Validate before calling
// Go: verify the fn value before passing to beam.ParDo/beam.Combine
func isFnValue(v interface{}) bool {
t := reflect.TypeOf(v)
if t == nil { return false }
switch t.Kind() {
case reflect.Func:
return true
case reflect.Ptr:
return t.Elem().Kind() == reflect.Struct
default:
return false
}
}
if !isFnValue(fn) { return fmt.Errorf("%T is not a func or ptr-to-struct", fn) } Type guard
func isFnValue(v interface{}) bool {
if v == nil { return false }
t := reflect.TypeOf(v)
if t.Kind() == reflect.Func { return true }
return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
} Try / catch
// Go: fail fast with context when registering DoFns
if _, err := graph.NewFn(fn); err != nil {
return fmt.Errorf("registering DoFn %T: %w", fn, err)
} Prevention
- Pass function values directly, never `&someFunc`.
- Struct-based DoFns/CombineFns must be passed as pointers to structs.
- Never pass pointers to maps, slices, or scalars as DoFn values.
When it happens
Trigger: Passing e.g. `&myInt`, `&slice`, or another pointer-to-non-struct to beam.ParDo/beam.Combine (which call NewFn), or a typed nil pointer to a non-struct.
Common situations: Typos like passing `&fn` (pointer to a func variable) instead of `fn`; passing a pointer to a map/slice created for config; mixing up a DoFn struct instance with a helper object.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- method invalid
- cannot convert to schema. FromType only converts structs to…
- failed to find method
- ProcessElement uses a StateProvider, but no State structs…
- unable to get row encoder
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/2ebf5cf9e90593e0.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/core/graph/fn.go:102
f, err := funcx.New(gen.Gen(gen.Name, gen.T, gen.Data))
if err != nil {
return nil, err
}
return &Fn{Fn: f, DynFn: gen}, nil
}
val := reflect.ValueOf(fn)
switch val.Type().Kind() {
case reflect.Func:
f, err := funcx.New(reflectx.MakeFunc(fn))
if err != nil {
return nil, err
}
return &Fn{Fn: f}, nil
case reflect.Ptr:
if val.Elem().Kind() != reflect.Struct {
return nil, errors.Errorf("value %v must be ptr to struct", fn)
}
// Note that a ptr receiver is necessary if struct fields are updated in the
// user code. Otherwise, updates are simply lost.
fallthrough
case reflect.Struct:
methods := make(map[string]*funcx.Fn)
annotations := make(map[string][]byte)
af := reflect.Indirect(val).FieldByName("Annotations")
if af.IsValid() {
a, ok := af.Interface().(map[string][]byte)
if ok {
annotations = a
}
}
if methodsFuncs, ok := reflectx.WrapMethods(fn); ok {
for name, mfn := range methodsFuncs {View on GitHub (pinned to 12126d8942)