micro/go-micro · warning
%s-%s | %s
Error message
%s-%s | %s
What it means
ServeConn's receive loop sets a global error when sock.Recv fails, wrapping it with the server name, ID, and the remote peer address in the format "<name>-<id> | <remote>: <cause>". Because a Recv failure means the socket is no longer usable, the loop returns immediately and the connection is torn down.
Source
Thrown at server/rpc_server.go:154
}
}()
for {
msg := transport.Message{
Header: make(map[string]string),
}
// Close connection if Connection: close header was set
if closeConn {
return
}
// Process inbound messages one at a time
if err := sock.Recv(&msg); err != nil {
// Set a global error and return.
// We're saying we essentially can't
// use the socket anymore
gerr = errors.Wrapf(err, "%s-%s | %s", s.opts.Name, s.opts.Id, sock.Remote())
return
}
// Keep track of when to close the connection
if c := msg.Header["Connection"]; c == "close" {
closeConn = true
}
// Check the message header for micro message header, if so handle
// as micro event
if t := msg.Header[headers.Message]; len(t) > 0 {
// Process the event
ev := newEvent(msg)
if err := s.HandleEvent(ev.Topic())(ev); err != nil {
msg.Header[headers.Error] = err.Error()
logger.Logf(log.ErrorLevel, "failed to handle event: %v", err)View on GitHub (pinned to 24529f1404)
Solutions
- Check the wrapped cause (errors.Cause) — ErrConnClosed / io.EOF are normal disconnects
- Treat these as client-side lifecycle events and log at debug rather than error for normal closes
- Investigate client idle/keepalive settings if disconnects are frequent
- Ensure the transport has adequate timeouts for long-running requests
- Verify load balancers/proxies aren't terminating idle connections
Example fix
// before
// server logs noisy errors on every normal client disconnect
log.Errorf("%v", gerr)
// after
cause := errors.Cause(err)
if errors.Is(cause, io.EOF) || errors.Is(cause, transport.ErrConnClosed) {
log.Debugf("client %s disconnected", sock.Remote())
} else {
log.Errorf("%v", gerr)
} Defensive patterns
Strategy: try-catch
Validate before calling
// client side: keepalive so the server doesn't see unexpected dead peers sock.SetTimeout(10 * time.Second) // send a close header when done msg.Header["Connection"] = "close"
Try / catch
if err := server.Start(); err != nil { ... }
// server-side: wrap the ServeConn error and classify
gerr := errors.Wrapf(err, "%s-%s | %s", name, id, remote)
if errors.Is(errors.Cause(err), io.EOF) || errors.Is(errors.Cause(err), transport.ErrConnClosed) {
log.Debugf("peer %s disconnected", remote)
} else {
log.Errorf("%v", gerr)
} Prevention
- Configure client keepalive/idle timeouts shorter than any LB/proxy idle timeout
- Send Connection: close header on graceful client shutdown
- Classify recv errors: EOF/reset are routine, others need investigation
- Use transport-level health checks to detect dying connections early
When it happens
Trigger: Any failure receiving from the client socket during message processing: the client disconnected/closed the connection, network timeout, socket closed by shutdown, or a transport-level error (e.g. connection reset by peer).
Common situations: Client crash or abrupt disconnect mid-request; client idle timeouts; load balancer killing long-lived connections; server shutdown closing sockets; network partitions between client and server.
Related errors
- connection header set to close
- Unable to encode body
- go.micro.client
- streamer not implemented
- rpc Register: type ${sname} is not exported
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/742169c83601c95a.
Report an issue: GitHub.