apache/pulsar · error

function may not return more than two values

Error message

function may not return more than two values

What it means

validateReturns restricts handler return arity: a Go function may return either nothing, one error, or (result, error). A handler whose reflect type has NumOut() > 2 cannot be invoked correctly by the framework, so newFunction fails with this error.

Source

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

		return false, fmt.Errorf("functions may not take more than two arguments, but function takes %d", handler.NumIn())
	} else if handler.NumIn() > 0 {
		contextType := reflect.TypeOf((*context.Context)(nil)).Elem()
		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)

View on GitHub (pinned to 820761864e)

Solutions

  1. Return at most two values: a single output of type T plus an error.
  2. Pack extra outputs into the single result (struct, map, or serialized payload).
  3. Log diagnostics instead of returning them; publish additional data to other topics via the output topic.
  4. Adapt with a wrapper that drops/merges extra return values into one.

Example fix

// before
func handle(ctx context.Context, msg []byte) ([]byte, []byte, error) { ... }
// after
type result struct{ Out []byte; Extra []byte }
func handle(ctx context.Context, msg []byte) (result, error) { ... }
Defensive patterns

Strategy: validation

Validate before calling

func validReturnArity(handler interface{}) bool {
    t := reflect.TypeOf(handler)
    return t != nil && t.Kind() == reflect.Func && t.NumOut() <= 2
}

Type guard

func returnsAtMostTwo(handler interface{}) bool {
    t := reflect.TypeOf(handler)
    return t.Kind() == reflect.Func && t.NumOut() <= 2
}

Try / catch

f, err := pf.NewGoFunction(ctx, pf.GoFunction{
    Handler: myHandler,
})
if err != nil {
    log.Fatalf("invalid handler returns: %v", err)
}

Prevention

When it happens

Trigger: Registering a handler returning three or more values, e.g. func(ctx context.Context, msg []byte) ([]byte, []byte, error), or returning multiple results plus error.

Common situations: Trying to return both an output message and extra diagnostics; porting handlers that return (value, metadata, error); misunderstanding that only a single output plus error is supported.

Related errors


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