{"record":{"id":"6feca8a67c210814","repo":"micro/go-micro","slug":"connection-pool-is-closed","errorCode":null,"errorMessage":"connection pool is closed","messagePattern":"connection pool is closed","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"transport/nats/pool.go","lineNumber":15,"sourceCode":"package nats\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\t\"time\"\n\n\tnatsp \"github.com/nats-io/nats.go\"\n)\n\nvar (\n\t// ErrPoolExhausted is returned when no connections are available in the pool\n\tErrPoolExhausted = errors.New(\"connection pool exhausted\")\n\t// ErrPoolClosed is returned when trying to use a closed pool\n\tErrPoolClosed = errors.New(\"connection pool is closed\")\n)\n\n// connectionPool manages a pool of NATS connections\ntype connectionPool struct {\n\tmu          sync.RWMutex\n\tconnections chan *pooledConnection\n\tfactory     func() (*natsp.Conn, error)\n\tsize        int\n\tidleTimeout time.Duration\n\tclosed      bool\n}\n\n// pooledConnection wraps a NATS connection with metadata\ntype pooledConnection struct {\n\tconn      *natsp.Conn\n\tcreatedAt time.Time\n\tlastUsed  time.Time\n\tmu        sync.Mutex","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/transport/nats/pool.go#L1-L33","documentation":"ErrPoolClosed is returned when Get (or TestConnectionPool-style checks) is called on a connection pool whose Close() has already run. The pool marks itself closed and drains its connection channel, so subsequent use is rejected instead of silently failing.","triggerScenarios":"Calling pool.Get()/Dial after pool.Close(); using a transport whose shared pool was closed by another component; calling TestConnectionPool_Close flows where the pool is closed while still referenced; races between shutdown and in-flight requests.","commonSituations":"Graceful shutdown closing the pool while workers still process requests; double Close in cleanup; singleton transport closed by one code path while others keep dialing; tests closing a shared pool fixture early.","solutions":["Serialize shutdown: stop all workers before calling pool.Close()","Re-create the transport/pool if it was closed but is still needed","Guard call sites with a state check or errors.Is(err, ErrPoolClosed) fallback to re-dial","Avoid closing shared transports owned by another component"],"exampleFix":"// before\npool.Close()\nconn, err := pool.Get(ctx) // ErrPoolClosed\n// after\nif err := pool.Close(); err != nil { _ = err }\n// re-create pool or gate Get behind shutdown signal:\nselect {\ncase <-shutdown: return\ndefault:\n\tconn, err = pool.Get(ctx)\n}","handlingStrategy":"try-catch","validationCode":"func poolUsable(closed atomic.Bool) bool { return !closed.Load() }\n// if !poolUsable(poolClosed) { conn, err = pool.Get(ctx) } else { re-create pool }","typeGuard":"func isPoolClosed(err error) bool { return errors.Is(err, nats.ErrPoolClosed) }","tryCatchPattern":"conn, err := pool.Get(ctx)\nif isPoolClosed(err) {\n\tpool, err = newPool() // recreate after close\n\tif err != nil { return err }\n\tconn, err = pool.Get(ctx)\n}","preventionTips":["Close pools only after all workers have stopped (use WaitGroup)","Track pool lifecycle with a single owner; avoid double Close","On ErrPoolClosed, recreate the pool rather than retrying on the same one","Coordinate shutdown via context cancellation before closing"],"tags":["nats","connection-pool","closed-resource","lifecycle"],"backgroundTag":"use-of-closed-connection","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}