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
- Create subscribers with `server.NewSubscriber(topic, handler, opts...)` from the same package/version as the server and pass that to Subscribe
- Check that the value is non-nil before calling Subscribe
- 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
- Always construct subscribers with server.NewSubscriber, never implement the Subscriber interface by hand
- Guard against passing nil subscriber variables before calling Subscribe
- Keep all go-micro packages on one version so interface implementations match
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
- subscriber %v takes wrong number of args: %v required signat
- subscriber %v argument type not exported: %v
- subscriber %v has wrong number of outs: %v require signature
- subscriber %v returns %v not error
- subscriber %v.%v takes wrong number of args: %v required sig
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/27a489185fc05953.
Report an issue: GitHub.