micro/go-micro · error
subscriber %v returns %v not error
Error message
subscriber %v returns %v not error
What it means
Beyond returning exactly one value, a subscriber's single return must be the built-in error interface type. validateSubscriber compares typ.Out(0) against typeOfError (reflect.TypeOf((*error)(nil)).Elem()) and rejects anything else, because the dispatcher type-asserts the first return value to error (returnValues[0].Interface().(error)).
Source
Thrown at server/grpc/subscriber.go:135
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)
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)View on GitHub (pinned to 24529f1404)
Solutions
- Change the return to the standard error interface: ... error.
- Wrap custom error types in error on return (return myErr where myErr implements error, or fmt.Errorf with %w).
- Return nil explicitly when there is no failure.
- If you need rich failure info, attach it to a custom error type that satisfies the error interface rather than changing the return type.
Example fix
// before
func(ctx context.Context, e *pb.Event) *EventError { ... }
// after
func(ctx context.Context, e *pb.Event) error { ...; return &EventError{...} } Defensive patterns
Strategy: validation
Validate before calling
func returnsStdError(fn interface{}) bool {
t := reflect.TypeOf(fn)
if t == nil || t.Kind() == reflect.Func && t.NumOut() != 1 { return false }
return t.NumOut() == 1 && t.Out(0) == reflect.TypeOf((*error)(nil)).Elem()
} Prevention
- Only ever return the built-in error interface from subscribers.
- Custom error types may be returned as values of type error, not as their own type.
- Avoid project-specific result/error wrapper types as subscriber return values.
- Add a compile-time assertion: var _ func(context.Context, *pb.Event) error = myHandler.
When it happens
Trigger: Registering a subscriber whose sole return is a concrete type or other interface, e.g. func(ctx context.Context, e *T) *MyError, or returns a custom error interface, string, or bool instead of error.
Common situations: Handlers written with a project-specific error type for consistency with internal APIs; refactoring that replaced error with a wrapped result type; porting handlers from frameworks that allow arbitrary returns.
Related errors
- subscriber %v takes wrong number of args: %v required signat
- subscriber %v has wrong number of outs: %v require signature
- subscriber %v.%v takes wrong number of args: %v required sig
- subscriber %v argument type not exported: %v
- %v argument type not exported: %v
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/609b5aab0fec966e.
Report an issue: GitHub.