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, err

View on GitHub (pinned to 24529f1404)

Solutions

  1. Verify the server/listener was not closed before dialing; re-Listen and dial the new address if it was
  2. Treat 'connection error' as a transient failure and retry with a fresh lookup of the current listener address
  3. 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

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


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