apache/pulsar · error

function takes two arguments, but the first is not Context.

Error message

function takes two arguments, but the first is not Context. got %s

What it means

When a Go function handler declares two input parameters, the framework requires the first to implement context.Context so it can inject the function context. Reflection checks argumentType.Implements(contextType); a non-Context first parameter fails registration with this error, reporting the parameter's reflect.Kind.

Source

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

	return output, nil
}

func errorHandler(e error) pulsarFunction {
	return func(ctx context.Context, input []byte) ([]byte, error) {
		return nil, e
	}
}

func validateArguments(handler reflect.Type) (bool, error) {
	handlerTakesContext := false
	if handler.NumIn() > 2 {
		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) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Change the first parameter to ctx context.Context (and put the payload second).
  2. If no context is needed, use a single-parameter handler func(msg T).
  3. Ensure any custom context type embeds/implements context.Context.
  4. Adapt legacy signatures with a closure: func(ctx context.Context, msg []byte) { return legacy(msg, ...) }.

Example fix

// before
func handle(msg []byte, ctx *MyContext) ([]byte, error) { ... }
// after
func handle(ctx context.Context, msg []byte) ([]byte, error) { ... }
Defensive patterns

Strategy: validation

Validate before calling

func takesContextFirst(handler interface{}) bool {
    t := reflect.TypeOf(handler)
    if t == nil || t.Kind() != reflect.Func || t.NumIn() == 0 {
        return false
    }
    ctxType := reflect.TypeOf((*context.Context)(nil)).Elem()
    if t.NumIn() == 1 {
        return true // single-arg handlers are fine regardless
    }
    return t.In(0).Implements(ctxType)
}

Type guard

func firstArgIsContext(handler interface{}) bool {
    t := reflect.TypeOf(handler)
    ctxType := reflect.TypeOf((*context.Context)(nil)).Elem()
    return t.Kind() == reflect.Func && t.NumIn() > 1 && t.In(0).Implements(ctxType)
}

Try / catch

f, err := pf.NewGoFunction(ctx, pf.GoFunction{
    Handler: myHandler,
})
if err != nil {
    if strings.Contains(err.Error(), "first is not Context") {
        log.Fatalf("handler signature invalid: first parameter must be context.Context: %v", err)
    }
    log.Fatalf("function registration failed: %v", err)
}

Prevention

When it happens

Trigger: A handler like func handle(msg []byte, id int) or func handle(cfg Config, msg []byte) is passed to newFunction; NumIn() == 2 but the first parameter does not implement context.Context, so the signature is rejected.

Common situations: Writing handlers as (payload, metadata) instead of (ctx, payload); parameter order swapped; using a custom context type that does not implement context.Context; porting functions from other runtimes.

Related errors


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