apache/pulsar · critical

function is nil

Error message

function is nil

What it means

pulsar-function-go wraps the user-supplied Go function into an internal `function` interface via newFunction. If the value passed to pf.Start() is nil, no valid handler can be built, so the library returns an errorHandler function that always yields this error instead of panicking at construction time. The failure surfaces when the instance actually starts and tries to process messages.

Source

Thrown at pulsar-function-go/pf/function.go:99

	switch {
	case handler.NumOut() > 2:
		return fmt.Errorf("function may not return more than two values")
	case handler.NumOut() > 1:
		if !handler.Out(1).Implements(errorType) {
			return fmt.Errorf("function returns two values, but the second does not implement error")
		}
	case handler.NumOut() == 1:
		if !handler.Out(0).Implements(errorType) {
			return fmt.Errorf("function returns a single value, but it does not implement error")
		}
	}

	return nil
}

func newFunction(inputFunc interface{}) function {
	if inputFunc == nil {
		return errorHandler(fmt.Errorf("function is nil"))
	}
	handler := reflect.ValueOf(inputFunc)
	handlerType := reflect.TypeOf(inputFunc)
	if handlerType.Kind() != reflect.Func {
		return errorHandler(fmt.Errorf("function kind %s is not %s", handlerType.Kind(), reflect.Func))
	}

	takesContext, err := validateArguments(handlerType)
	if err != nil {
		return errorHandler(err)
	}

	if err := validateReturns(handlerType); err != nil {
		return errorHandler(err)
	}

	return pulsarFunction(func(ctx context.Context, input []byte) ([]byte, error) {
		// construct arguments

View on GitHub (pinned to 820761864e)

Solutions

  1. Pass a non-nil function literal or named function to pf.Start(myFunc).
  2. Check the variable holding your handler before calling Start: if myFunc == nil { log.Fatal(...) }.
  3. If the handler comes from dynamic dispatch, ensure the registration/assignment path runs before Start.

Example fix

// before
var handler func(context.Context, []byte) ([]byte, error)
pf.Start(handler) // nil -> "function is nil"
// after
var handler func(context.Context, []byte) ([]byte, error) = actualHandler
if handler == nil {
    log.Fatal("no function handler configured")
}
pf.Start(handler)
Defensive patterns

Strategy: validation

Validate before calling

if fn == nil {
    return fmt.Errorf("handler must be a non-nil func before calling pf.Start")
}
pf.Start(fn)

Type guard

func isFunc(v interface{}) bool {
    return v != nil && reflect.TypeOf(v).Kind() == reflect.Func
}

Prevention

When it happens

Trigger: Calling pf.Start(nil), or passing a nil func variable (e.g. a func obtained from config/flag that was never assigned).

Common situations: A variable holding the handler is conditionally assigned; a refactor renamed the handler function leaving a nil default; loading the handler dynamically and the lookup returned nil.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/e6daea4e2400fbe6. Report an issue: GitHub.