micro/go-micro · error
subscriber %v.%v returns %v not error
Error message
subscriber %v.%v returns %v not error
What it means
After checking the handler returns exactly one value, validateSubscriber verifies that the single return type is exactly the built-in `error` interface. If the handler returns something else (a custom error type, a string, a struct, etc.) registration fails with this message naming the offending return type.
Source
Thrown at server/grpc/subscriber.go:161
switch method.Type.NumIn() {
case 3:
argType = method.Type.In(2)
default:
return fmt.Errorf("subscriber %v.%v takes wrong number of args: %v required signature %s",
name, method.Name, method.Type.NumIn(), subSig)
}
if !isExportedOrBuiltinType(argType) {
return fmt.Errorf("%v argument type not exported: %v", name, argType)
}
if method.Type.NumOut() != 1 {
return fmt.Errorf(
"subscriber %v.%v has wrong number of outs: %v require signature %s",
name, method.Name, method.Type.NumOut(), subSig)
}
if returnType := method.Type.Out(0); returnType != typeOfError {
return fmt.Errorf("subscriber %v.%v returns %v not error", name, method.Name, returnType.String())
}
}
}
return nil
}
func (g *grpcServer) createSubHandler(sb *subscriber, opts server.Options) broker.Handler {
return func(p broker.Event) (err error) {
defer func() {
if r := recover(); r != nil {
g.opts.Logger.Log(logger.ErrorLevel, "panic recovered: ", r)
g.opts.Logger.Log(logger.ErrorLevel, string(debug.Stack()))
err = errors.InternalServerError("go.micro.server", "panic recovered: %v", r)
}
}()
msg := p.Message()View on GitHub (pinned to 24529f1404)
Solutions
- Change the return type to the built-in `error` interface: `func (h *h) Handle(ctx context.Context, e *Event) error`
- If a custom error type carries extra data, return it wrapped as `error` (implement/assign to error) and unwrap at the call site
Example fix
// before
func (h *handler) Handle(ctx context.Context, e *Event) *ValidationError { ... }
// after
func (h *handler) Handle(ctx context.Context, e *Event) error { ... } Defensive patterns
Strategy: validation
Validate before calling
func returnsError(fn interface{}) bool {
t := reflect.TypeOf(fn)
if t.NumOut() != 1 { return false }
errIface := reflect.TypeOf((*error)(nil)).Elem()
return t.Out(0) == errIface
} Prevention
- Return the built-in `error` interface, never a concrete error struct type
- If using a custom error type, return it as `error` and unwrap with errors.As in the caller
- Add a compile-time assertion or lint rule that subscriber handlers match the required signature
When it happens
Trigger: Registering a subscriber whose method returns a single value that is not of interface type `error`, e.g. `func (h *h) Handle(ctx context.Context, e *Event) *MyError` or returns a concrete error implementation or a status/bool.
Common situations: Handlers returning concrete custom error structs instead of the `error` interface; converting code from other frameworks where handlers return status objects or nothing.
Related errors
- subscriber %v.%v has wrong number of outs: %v require signat
- 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
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/f7b3fe930abe4404.
Report an issue: GitHub.