micro/go-micro · error

${rsp.Status}: ${body} (dynamic HTTP status and body)

Error message

${rsp.Status}: ${body} (dynamic HTTP status and body)

What it means

When an HTTP response's status code is anything other than 200 OK, Recv reads the whole body and returns an error containing '<status string>: <body>'. The error message is dynamic: the transport surfaces the upstream HTTP status and response body directly as the error text, so non-OK responses never reach your Message.

Source

Thrown at transport/http_client.go:169

	rsp, err := http.ReadResponse(h.buff, req)
	if err != nil {
		return err
	}

	defer func() {
		if err2 := rsp.Body.Close(); err2 != nil {
			err = errors.Wrap(err2, "failed to close body")
		}
	}()

	b, err := io.ReadAll(rsp.Body)
	if err != nil {
		return err
	}

	if rsp.StatusCode != http.StatusOK {
		return errors.New(rsp.Status + ": " + string(b))
	}

	msg.Body = b

	if msg.Header == nil {
		msg.Header = make(map[string]string, len(rsp.Header))
	}

	for k, v := range rsp.Header {
		if len(v) > 0 {
			msg.Header[k] = v[0]
		} else {
			msg.Header[k] = ""
		}
	}

	return nil
}

View on GitHub (pinned to 24529f1404)

Solutions

  1. Log err.Error() — it embeds the HTTP status and the server's error body; fix the root cause the status indicates (auth, path, payload)
  2. On the client side, match the status prefix (e.g. strings.HasPrefix(err.Error(), "401")) if you need to branch, or prefer the socket/stream API that exposes headers
  3. Fix the server side so healthy requests return 200; add health checks/retries for transient 5xx

Example fix

// before
if err := client.Recv(msg); err != nil { return err } // "401 Unauthorized: {\"error\":...}" opaque
// after
if err := client.Recv(msg); err != nil {
	if strings.HasPrefix(err.Error(), "401") { return ErrUnauthorized }
	return err
}
Defensive patterns

Strategy: try-catch

Try / catch

if err := client.Recv(msg); err != nil {
	for _, code := range []string{"401","403","404","500"} {
		if strings.HasPrefix(err.Error(), code) { /* handle status */ }
	}
	return err
}

Prevention

When it happens

Trigger: Any Recv over a plain (non-stream) request where the server replies 4xx/5xx — auth failures (401/403), not found (404), validation errors (400), or backend crashes (500).

Common situations: Calling a service whose endpoint moved (404), expired credentials (401), or a downstream service returning JSON error bodies that end up concatenated into the Go error string; parsing code that expects msg.Body gets nothing because the error path fired first.

Related errors


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