apache/beam · error

not a function: %v

Error message

not a function: %v

What it means

Satisfy in sdks/go/pkg/beam/core/funcx/signature.go validates that a value satisfies the funcx function contract. When the argument is neither a *funcx.Fn nor a reflect Func kind, it reflects on the value and, if value.Kind() != reflect.Func, returns "not a function: %v". The library throws it because signature analysis (NumIn/NumOut/parameter types) is only possible on actual function values.

Source

Thrown at sdks/go/pkg/beam/core/funcx/signature.go:122

//
//	foo : (context.Context, X) -> bool
//	bar : (int) -> bool
//
// both would satisfy a signature of (context.Context?, int) -> bool. Only
// "foo" would satisfy (context.Context, string) -> bool and only "bar" would
// satisfy (int) -> bool.
func Satisfy(fn any, sig *Signature) error {
	var in, out []reflect.Type
	var typ reflect.Type
	switch fx := fn.(type) {
	case *Fn:
		typ = fx.Fn.Type()
	case reflectx.Func:
		typ = fx.Type()
	default:
		value := reflect.ValueOf(fn)
		if value.Kind() != reflect.Func {
			return errors.Errorf("not a function: %v", value)
		}
		typ = value.Type()
	}
	for i := 0; i < typ.NumIn(); i++ {
		in = append(in, typ.In(i))
	}
	for i := 0; i < typ.NumOut(); i++ {
		out = append(out, typ.Out(i))
	}
	if len(in) < len(sig.Args) || len(out) < len(sig.Return) {
		return errors.Errorf("not enough required parameters: %v", typ)
	}

	if len(in) > len(sig.Args)+len(sig.OptArgs) || len(out) > len(sig.Return)+len(sig.OptReturn) {
		return errors.Errorf("too many parameters: %v", typ)
	}

	// (1) Create generic binding. If inconsistent, reject fn. We do not allow

View on GitHub (pinned to 12126d8942)

Solutions

  1. Pass an actual function value: `fx.Satisfy(myFunc)` not `fx.Satisfy(myFunc())`.
  2. If you have a *funcx.Fn, pass it directly — it is accepted via the fx.Fn branch.
  3. Check for nil or shadowed variables holding the wrong value before calling Satisfy.
  4. Wrap with fx.MustSatisfy only in tests/init; prefer fx.Satisfy and handle the returned error in production paths.

Example fix

// before
fn := buildFn() // returns bool, not a func
fx.MustSatisfy(fn)
// after
fn := buildFn   // the function value itself
fx.MustSatisfy(fn)
Defensive patterns

Strategy: type-guard

Validate before calling

func requireFunc(v interface{}) error {
	if v == nil {
		return errors.New("fn is nil")
	}
	if _, ok := v.(*fx.Fn); ok {
		return nil
	}
	if reflect.TypeOf(v).Kind() != reflect.Func {
		return fmt.Errorf("%T is not a function", v)
	}
	return nil
}

Type guard

func isFuncValue(v interface{}) bool {
	if v == nil {
		return false
	}
	if _, ok := v.(*fx.Fn); ok {
		return true
	}
	return reflect.TypeOf(v).Kind() == reflect.Func
}

Try / catch

defer func() {
	if r := recover(); r != nil {
		// MustSatisfy may panic on non-functions
		err = fmt.Errorf("signature check failed: %v", r)
	}
}()
// or avoid panic entirely:
if err := fx.Satisfy(fn); err != nil {
	return fmt.Errorf("not a function: %w", err)
}

Prevention

When it happens

Trigger: Calling fx.Satisfy / fx.MustSatisfy / validateEncoder-style helpers with a non-function value such as a struct, method value bound incorrectly, nil, a funcx.Fn-typed wrapper used in the wrong branch, or a variable that was expected to hold a function but holds its result (e.g. `fn := myFunc()` instead of `fn := myFunc`).

Common situations: Passing a closure's invocation result instead of the closure itself; passing a method call `obj.Method` written as `obj.Method()`; handing a nil interface to MustSatisfy in an init path; type confusion after refactoring a helper that returned a function.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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