apache/beam · error

value %v must be function or (ptr to) struct

Error message

value %v must be function or (ptr to) struct

What it means

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.

Source

Thrown at sdks/go/pkg/beam/core/graph/fn.go:149

			}
			m, ok := val.Type().MethodByName(mName)
			if !ok {
				continue // skip: doesn't exist
			}

			// CAVEAT(herohde) 5/22/2017: The type val.Type.Method.Type is not
			// the same as val.Method.Type: the former has the explicit receiver.
			// We'll use the receiver-less version.
			f, err := funcx.New(reflectx.MakeFunc(val.Method(m.Index).Interface()))
			if err != nil {
				return nil, errors.Wrapf(err, "method %v invalid", mName)
			}
			methods[mName] = f
		}
		return &Fn{Recv: fn, methods: methods, annotations: annotations}, nil

	default:
		return nil, errors.Errorf("value %v must be function or (ptr to) struct", fn)
	}
}

// Signature method names.
const (
	setupName          = "Setup"
	startBundleName    = "StartBundle"
	processElementName = "ProcessElement"
	finishBundleName   = "FinishBundle"
	teardownName       = "Teardown"

	createInitialRestrictionName = "CreateInitialRestriction"
	splitRestrictionName         = "SplitRestriction"
	restrictionSizeName          = "RestrictionSize"
	createTrackerName            = "CreateTracker"
	truncateRestrictionName      = "TruncateRestriction"

	createWatermarkEstimatorName       = "CreateWatermarkEstimator"

View on GitHub (pinned to 12126d8942)

Solutions

  1. Pass a function or a pointer to a struct: beam.ParDo(s, &myFn{}, in)
  2. If using a structural DoFn, take its address before passing
  3. Add a compile-time assertion like var _ = fn (func or *struct) in tests

Example fix

// before
beam.ParDo(s, MyFn{count: 3}, in)
// after
beam.ParDo(s, &MyFn{count: 3}, in)
Defensive patterns

Strategy: validation

Validate before calling

switch v := fn.(type) {
case func(...), *struct{}:
    // ok
default:
    return fmt.Errorf("fn %T must be func or *struct", fn)
}

Type guard

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) }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/ce136f6a71d1105e. Report an issue: GitHub.