micro/go-micro · error · errors.Error

go.micro.client

go.micro.client

Error message

go.micro.client

What it means

In client/grpc/error.go, microError converts a gRPC status error into a micro errors.Error. It first attempts to parse the message as an existing micro error; if that fails (code <= 0), it fabricates a new error with code "go.micro.client" and maps the gRPC status code to an HTTP-like status via microStatusFromGrpcCode. The message "go.micro.client" here is the error's Code/Id field, indicating a generic client-side gRPC failure.

Source

Thrown at client/grpc/error.go:39

	// grpc error
	s, ok := status.FromError(err)
	if !ok {
		return err
	}

	// return first error from details
	if details := s.Details(); len(details) > 0 {
		return microError(details[0].(error))
	}

	// try to decode micro *errors.Error
	if e := errors.Parse(s.Message()); e.Code > 0 {
		return e // actually a micro error
	}

	// fallback
	return errors.New("go.micro.client", s.Message(), microStatusFromGrpcCode(s.Code()))
}

func microStatusFromGrpcCode(code codes.Code) int32 {
	switch code {
	case codes.OK:
		return http.StatusOK
	case codes.InvalidArgument:
		return http.StatusBadRequest
	case codes.DeadlineExceeded:
		return http.StatusRequestTimeout
	case codes.NotFound:
		return http.StatusNotFound
	case codes.AlreadyExists:
		return http.StatusConflict
	case codes.PermissionDenied:
		return http.StatusForbidden
	case codes.Unauthenticated:
		return http.StatusUnauthorized

View on GitHub (pinned to 24529f1404)

Solutions

  1. Inspect the wrapped gRPC status (status.FromError) to see the real codes.Code and message
  2. Use errors.Parse-style handling: check err.Code and err.Id to classify the failure
  3. Add retry logic for transient codes (Unavailable, DeadlineExceeded) with backoff
  4. Ensure server responses use micro error formatting so codes round-trip correctly

Example fix

// before
err := client.Call(ctx, req, rsp)
if err != nil { return err }
// after
err := client.Call(ctx, req, rsp)
if err != nil {
    if e, ok := err.(*errors.Error); ok && e.Code == 504 {
        return retryWithBackoff(ctx, req, rsp)
    }
    return err
}
Defensive patterns

Strategy: type-guard

Validate before calling

if st, ok := status.FromError(err); ok && st.Code() == codes.Unavailable {
    // safe to retry
}

Type guard

func isMicroClientError(err error) (*errors.Error, bool) {
    e, ok := err.(*errors.Error)
    if !ok || e.Code == 0 { return nil, false }
    return e, true
}

Try / catch

err := client.Call(ctx, req, rsp)
if e, ok := err.(*errors.Error); ok {
    switch e.Code {
    case 504: return retry(ctx, req, rsp)
    case 503: return retryWithBackoff(ctx, req, rsp)
    default: return err
    }
}
return err

Prevention

When it happens

Trigger: Any gRPC call made through the micro grpc client that returns a status error whose message is not a parseable micro error — e.g. transport failures, Unavailable, DeadlineExceeded, or server errors not formatted as micro errors.

Common situations: Calling a non-micro gRPC server through the micro client; server crashing or rejecting the RPC; network partitions; deadline exceeded on slow backends.

Related errors


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