micro/go-micro · warning

unable to close in time

Error message

unable to close in time

What it means

pool.close in internal/util/pool/default.go closes a pooled client asynchronously and waits up to p.closeTimeout for the Close call to finish. If the timer fires before Close returns, the pool gives up and returns this error. It means the underlying client hung during shutdown.

Source

Thrown at internal/util/pool/default.go:154

		return conn.(*poolConn).close()
	}

	p.conns[conn.Remote()] = append(conns, conn.(*poolConn))

	return nil
}

func (p *poolConn) close() error {
	ch := make(chan error)
	go func() {
		defer close(ch)
		ch <- p.Client.Close()
	}()
	t := time.NewTimer(p.closeTimeout)
	var err error
	select {
	case <-t.C:
		err = errors.New("unable to close in time")
	case err = <-ch:
		t.Stop()
	}
	return err
}

View on GitHub (pinned to 24529f1404)

Solutions

  1. Increase the pool's closeTimeout option so slow-but-healthy clients have time to close.
  2. Ensure all in-flight calls/streaming RPCs are completed or cancelled before closing the pool.
  3. Check that the backend the client connects to is reachable and honoring connection close.
  4. Investigate the client's Close path for hangs (e.g. dial retries or un-drained streams) and fix the root cause.

Example fix

// before
pool, err := pool.NewPool(pool.CloseTimeout(500*time.Millisecond))
// after
pool, err := pool.NewPool(pool.CloseTimeout(5*time.Second))
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

if err := p.Close(); err != nil {
    if strings.Contains(err.Error(), "unable to close in time") {
        log.Warn("pool close timed out; client may still be draining", "timeout", p.closeTimeout)
        // proceed with shutdown; don't fail the whole process
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling pool.Close() (or letting a pool be torn down) when an underlying gRPC/client connection cannot close promptly — e.g. in-flight RPCs, a blocked transport, or a server that never responds to connection teardown.

Common situations: Test cleanup where a fake server never closes connections; production shutdown with live streaming RPCs; network partitions making the client's close handshake block until TCP timeouts.

Related errors


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