micro/go-micro · error
connection error
Error message
connection error
What it means
After constructing the client, Dial performs a 'pseudo connect': it pushes the client's socket into the listener's conn channel. If the listener has exited (server closed) instead of accepting, Dial returns the generic 'connection error'. It means the endpoint existed but shut down between lookup and connect.
Source
Thrown at transport/memory.go:218
&memorySocket{
server: false,
csend: gob.NewEncoder(cwriter),
crecv: gob.NewDecoder(creader),
ssend: gob.NewEncoder(swriter),
srecv: gob.NewDecoder(sreader), exit: make(chan bool),
lexit: listener.exit,
local: addr,
remote: addr,
timeout: m.opts.Timeout,
ctx: m.opts.Context,
},
options,
}
// pseudo connect
select {
case <-listener.exit:
return nil, errors.New("connection error")
case listener.conn <- client.memorySocket:
}
return client, nil
}
func (m *memoryTransport) Listen(addr string, opts ...ListenOption) (Listener, error) {
m.Lock()
defer m.Unlock()
var options ListenOptions
for _, o := range opts {
o(&options)
}
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, errView on GitHub (pinned to 24529f1404)
Solutions
- Verify the server/listener was not closed before dialing; re-Listen and dial the new address if it was
- Treat 'connection error' as a transient failure and retry with a fresh lookup of the current listener address
- In tests, stop clients before closing the listener, or serialize teardown with WaitGroups so Dial never races Close
Example fix
// before go server.Close() client, _ := m.Dial(addr) // may hit "connection error" // after client, _ := m.Dial(addr) use(client) // then closeClients() server.Close()
Defensive patterns
Strategy: retry
Try / catch
c, err := m.Dial(addr)
if err != nil && err.Error() == "connection error" {
// listener closed; re-Listen/re-resolve addr and retry once
} Prevention
- Close clients before closing the listener in teardown
- Don't cache memory-transport addresses across server restarts
- Serialize shutdown with WaitGroups to avoid Dial racing Close
When it happens
Trigger: Dialing a memory transport address whose server (memoryListener) has already been closed — Close() signals listener.exit, so any in-flight or subsequent Dial hits the exit case of the select.
Common situations: Race between a test tearing down the server and another goroutine still dialing it; reusing a stale address after server.Close(); shutdown ordering bugs where clients outlive the listener.
Related errors
- could not dial ${addr}
- already listening on ${addr}
- failed to close body
- connection header set to close
- failed request
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/82d7f98fdecba34d.
Report an issue: GitHub.