micro/go-micro · error

invalid connection from pool

Error message

invalid connection from pool

What it means

When the NATS broker is configured with a connection pool, Publish checks out a pooled connection and verifies its underlying *nats.Conn is non-nil. If the pool hands back an entry whose connection is nil (a stale or improperly released pooled entry), Publish returns this error instead of panicking on a nil dereference. It indicates pool corruption or a connection that was closed and zeroed out without being evicted.

Source

Thrown at broker/nats/nats.go:240

	n.RLock()
	defer n.RUnlock()

	b, err := n.opts.Codec.Marshal(msg)
	if err != nil {
		return err
	}

	// Use connection pool if enabled
	if n.pool != nil {
		poolConn, err := n.pool.Get()
		if err != nil {
			return err
		}
		defer func() { _ = n.pool.Put(poolConn) }()

		conn := poolConn.Conn()
		if conn == nil {
			return errors.New("invalid connection from pool")
		}
		return conn.Publish(topic, b)
	}

	// Use single connection (original behavior)
	if n.conn == nil {
		return errors.New("not connected")
	}

	return n.conn.Publish(topic, b)
}

func (n *natsBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
	n.RLock()
	hasConnection := n.conn != nil || n.pool != nil
	n.RUnlock()

	if !hasConnection {

View on GitHub (pinned to 24529f1404)

Solutions

  1. Call broker.Connect() again to rebuild the pool with fresh live connections, then retry Publish.
  2. Enable retry: reconnect the NATS client (nats.RetryOnFailedConnect, MaxReconnects) so closed pooled connections are re-established.
  3. Check for concurrent broker.Disconnect()/Close() racing Publish; guard shutdown with proper synchronization.
  4. Upgrade/patch the pool implementation so closed connections are evicted rather than reused.

Example fix

// before
if err := broker.Publish(ctx, "events", msg); err != nil {
    return err
}

// after
if err := broker.Publish(ctx, "events", msg); err != nil {
    if strings.Contains(err.Error(), "invalid connection from pool") {
        if cerr := broker.Connect(ctx); cerr != nil {
            return cerr
        }
        return broker.Publish(ctx, "events", msg)
    }
    return err
}
Defensive patterns

Strategy: retry

Validate before calling

// Before publishing, sanity-check NATS server reachability:
nc, err := nats.Connect(natsURL, nats.Timeout(2*time.Second))
if err != nil { return fmt.Errorf("nats unreachable: %w", err) }
if nc.Status() != nats.CONNECTED { return errors.New("nats not connected") }
nc.Close()

Type guard

func isInvalidPoolConn(err error) bool {
    return err != nil && strings.Contains(err.Error(), "invalid connection from pool")
}

Try / catch

err := br.Publish(ctx, topic, msg)
if err != nil && strings.Contains(err.Error(), "invalid connection from pool") {
    // rebuild pool / reconnect, then retry once
    if cerr := br.Connect(ctx); cerr == nil {
        err = br.Publish(ctx, topic, msg)
    }
}
return err

Prevention

When it happens

Trigger: Publishing with pooled mode enabled (n.pool != nil) when a pooled entry's Conn() returns nil — e.g. a pooled connection was closed elsewhere (NATS server restart, explicit Close) but its wrapper stayed in the pool at broker/nats/nats.go:240.

Common situations: NATS server restart or network drop that closes pooled connections while the broker keeps serving them from the pool; concurrent Close/Publish races; misconfigured pool warm-up creating placeholder entries with nil connections.

Related errors


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