apache/pulsar · error

function returns two values, but the second does not impleme

Error message

function returns two values, but the second does not implement error

What it means

For two-value handlers, the second return value must implement the error interface — the framework's (result, error) convention mirrors idiomatic Go. Reflection checks handler.Out(1).Implements(errorType); anything else (string, custom non-error type) fails registration.

Source

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

		argumentType := handler.In(0)
		handlerTakesContext = argumentType.Implements(contextType)
		if handler.NumIn() > 1 && !handlerTakesContext {
			return false, fmt.Errorf("function takes two arguments, but the first is not Context. got %s", argumentType.Kind())
		}
	}

	return handlerTakesContext, nil
}

func validateReturns(handler reflect.Type) error {
	errorType := reflect.TypeOf((*error)(nil)).Elem()

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

View on GitHub (pinned to 820761864e)

Solutions

  1. Make the second return value type error (return nil on success).
  2. Define custom error types so they implement error (add an Error() string method).
  3. If the second value is data, reorder so data is first and error second, or drop it.
  4. Wrap error-code strings into errors.New / fmt.Errorf.

Example fix

// before
func handle(ctx context.Context, msg []byte) ([]byte, string) { ... }
// after
func handle(ctx context.Context, msg []byte) ([]byte, error) {
    if bad { return nil, fmt.Errorf("bad message") }
    ...
}
Defensive patterns

Strategy: type-guard

Validate before calling

func secondReturnIsError(handler interface{}) bool {
    t := reflect.TypeOf(handler)
    errType := reflect.TypeOf((*error)(nil)).Elem()
    return t.Kind() == reflect.Func && t.NumOut() == 2 && t.Out(1).Implements(errType)
}

Type guard

func hasErrorSecond(handler interface{}) bool {
    t := reflect.TypeOf(handler)
    errType := reflect.TypeOf((*error)(nil)).Elem()
    return t.NumOut() > 1 && t.Out(1).Implements(errType)
}

Try / catch

f, err := pf.NewGoFunction(ctx, pf.GoFunction{
    Handler: myHandler,
})
if err != nil {
    if strings.Contains(err.Error(), "second does not implement error") {
        log.Fatalf("change second return value to type error: %v", err)
    }
    log.Fatalf("function registration failed: %v", err)
}

Prevention

When it happens

Trigger: A handler like func(ctx context.Context, msg []byte) ([]byte, string) or ([]byte, MyStatus) is registered; NumOut()==2 but Out(1) does not implement error.

Common situations: Returning a status/error code string instead of error; returning (value, value) pairs; custom error-like struct that does not implement the built-in error interface; accidentally swapping return order.

Related errors


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