micro/go-micro · error

invalid subscriber: expected *subscriber

Error message

invalid subscriber: expected *subscriber

What it means

rpcServer.Subscribe accepts only the library-internal `*subscriber` implementation of the Subscriber interface. Passing any other value — a custom type that implements the interface or a nil value — is rejected with this error because the server needs internal fields (topic, handlers) it can only access on the concrete type.

Source

Thrown at server/rpc_events.go:89

				return handler(ctx, msg)
			}}
		}

		return r.ProcessMessage(ctx, subscriber, rpcMsg)
	}
}

func (s *rpcServer) NewSubscriber(topic string, sb interface{}, opts ...SubscriberOption) Subscriber {
	return s.router.NewSubscriber(topic, sb, opts...)
}

func (s *rpcServer) Subscribe(sb Subscriber) error {
	s.Lock()
	defer s.Unlock()

	sub, ok := sb.(*subscriber)
	if !ok {
		return fmt.Errorf("invalid subscriber: expected *subscriber")
	}
	if len(sub.handlers) == 0 {
		return fmt.Errorf("invalid subscriber: no handler functions")
	}

	if err := validateSubscriber(sub); err != nil {
		return err
	}

	// append to subscribers
	// subs := s.subscribers[sub.Topic()]
	// subs = append(subs, sub)
	// router.subscribers[sub.Topic()] = subs

	s.subscribers[sb] = nil

	return nil
}

View on GitHub (pinned to 24529f1404)

Solutions

  1. Create subscribers with `server.NewSubscriber(topic, handler, opts...)` from the same package/version as the server and pass that to Subscribe
  2. Check that the value is non-nil before calling Subscribe
  3. Use the same go-micro server package throughout (do not mix grpc and default server subscriber types)

Example fix

// before
var sb server.Subscriber
server.Subscribe(sb) // nil / foreign type
// after
sb, err := server.NewSubscriber("topic", handler, server.SubscriberQueue("q"))
if err != nil { return err }
return server.Subscribe(sb)
Defensive patterns

Strategy: type-guard

Validate before calling

if sb == nil {
	return errors.New("subscriber must be created via server.NewSubscriber")
}

Type guard

func isInternalSubscriber(sb server.Subscriber) bool {
	_, ok := sb.(*server.Subscriber) // concrete internal type used by the running server
	return ok
}

Try / catch

if err := server.Subscribe(sb); err != nil {
	if strings.Contains(err.Error(), "invalid subscriber: expected *subscriber") {
		// rebuild via server.NewSubscriber before retrying
	}
}

Prevention

When it happens

Trigger: Calling `server.Subscribe(x)` where x is nil, a Subscriber from a different go-micro/server implementation, or a hand-rolled type implementing the Subscriber interface instead of one created by `server.NewSubscriber`.

Common situations: Mixing servers/subscribers from different micro frameworks or versions; passing a nil variable; writing a mock or custom Subscriber for tests and passing it to the real server.

Related errors


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