micro/go-micro · error

subscriber %v argument type not exported: %v

Error message

subscriber %v argument type not exported: %v

What it means

validateSubscriber reflects on the subscriber function's message argument and requires its type to be exported (or a builtin). The gRPC server must construct and decode values of this type from incoming wire messages, and unexported types defined inside another package cannot be instantiated or named by the codec, so registration fails.

Source

Thrown at server/grpc/subscriber.go:128

		endpoints:  endpoints,
		opts:       options,
	}
}

func validateSubscriber(sub server.Subscriber) error {
	typ := reflect.TypeOf(sub.Subscriber())
	var argType reflect.Type

	if typ.Kind() == reflect.Func {
		name := "Func"
		switch typ.NumIn() {
		case 2:
			argType = typ.In(1)
		default:
			return fmt.Errorf("subscriber %v takes wrong number of args: %v required signature %s", name, typ.NumIn(), subSig)
		}
		if !isExportedOrBuiltinType(argType) {
			return fmt.Errorf("subscriber %v argument type not exported: %v", name, argType)
		}
		if typ.NumOut() != 1 {
			return fmt.Errorf("subscriber %v has wrong number of outs: %v require signature %s",
				name, typ.NumOut(), subSig)
		}
		if returnType := typ.Out(0); returnType != typeOfError {
			return fmt.Errorf("subscriber %v returns %v not error", name, returnType.String())
		}
	} else {
		hdlr := reflect.ValueOf(sub.Subscriber())
		name := reflect.Indirect(hdlr).Type().Name()

		for m := 0; m < typ.NumMethod(); m++ {
			method := typ.Method(m)

			switch method.Type.NumIn() {
			case 3:
				argType = method.Type.In(2)

View on GitHub (pinned to 24529f1404)

Solutions

  1. Export the message type (capitalize it, e.g. type Event struct) so it is visible outside its package.
  2. Use the generated protobuf message types (pb.XxxRequest/pb.XxxEvent) as the payload instead of local structs.
  3. If the type is in an internal/ package, move it to an importable exported package.
  4. If it must stay unexported, wrap it: declare an exported wrapper struct containing the payload.

Example fix

// before
func(ctx context.Context, e *eventPayload) error { ... } // unexported
// after
type EventPayload struct{ ... }
func(ctx context.Context, e *EventPayload) error { ... }
Defensive patterns

Strategy: validation

Validate before calling

func exportedArg(fn interface{}) bool {
	t := reflect.TypeOf(fn)
	if t == nil || t.Kind() != reflect.Func || t.NumIn() != 2 { return false }
	a := t.In(1)
	if a.Kind() == reflect.Pointer { a = a.Elem() }
	return a.PkgPath() == "" // empty PkgPath => builtin or exported
}

Prevention

When it happens

Trigger: Passing a subscriber func whose second parameter is an unexported type, e.g. func(ctx context.Context, e *myPrivateEvent) error where myPrivateEvent (or the package containing it) starts with a lowercase letter and lives outside the registering package.

Common situations: Defining message structs in an internal package or as unexported types next to the handler; accidentally using a lowercase type name for what should be a public proto-generated message; moving a handler into a shared package without exporting its payload type.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/22d7347e52be77dd. Report an issue: GitHub.