{"record":{"id":"82d7f98fdecba34d","repo":"micro/go-micro","slug":"connection-error","errorCode":null,"errorMessage":"connection error","messagePattern":"connection error","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"transport/memory.go","lineNumber":218,"sourceCode":"\t\t&memorySocket{\n\t\t\tserver: false,\n\t\t\tcsend:  gob.NewEncoder(cwriter),\n\t\t\tcrecv:  gob.NewDecoder(creader),\n\t\t\tssend:  gob.NewEncoder(swriter),\n\t\t\tsrecv:  gob.NewDecoder(sreader), exit: make(chan bool),\n\t\t\tlexit:   listener.exit,\n\t\t\tlocal:   addr,\n\t\t\tremote:  addr,\n\t\t\ttimeout: m.opts.Timeout,\n\t\t\tctx:     m.opts.Context,\n\t\t},\n\t\toptions,\n\t}\n\n\t// pseudo connect\n\tselect {\n\tcase <-listener.exit:\n\t\treturn nil, errors.New(\"connection error\")\n\tcase listener.conn <- client.memorySocket:\n\t}\n\n\treturn client, nil\n}\n\nfunc (m *memoryTransport) Listen(addr string, opts ...ListenOption) (Listener, error) {\n\tm.Lock()\n\tdefer m.Unlock()\n\n\tvar options ListenOptions\n\tfor _, o := range opts {\n\t\to(&options)\n\t}\n\n\thost, port, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn nil, err","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/transport/memory.go#L200-L236","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ngo server.Close()\nclient, _ := m.Dial(addr) // may hit \"connection error\"\n// after\nclient, _ := m.Dial(addr)\nuse(client)\n// then\ncloseClients()\nserver.Close()","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"c, err := m.Dial(addr)\nif err != nil && err.Error() == \"connection error\" {\n\t// listener closed; re-Listen/re-resolve addr and retry once\n}","preventionTips":["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"],"tags":["transport","memory","connection","race"],"backgroundTag":"connection-closed","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}