panjf2000/ants · warning

pool %d: %v

Error message

pool %d: %v

What it means

This is a wrapped error produced by MultiPool.ReleaseContext (multipool.go:60): each sub-pool's ReleaseContext runs concurrently, and if any fails, the sub-pool's index and original error are combined as `pool %d: %v` via fmt.Errorf. It tells you which sub-pool of a MultiPool failed during graceful release (e.g. it was already closed or the drain context expired).

Source

Thrown at multipool.go:60

	RoundRobin LoadBalancingStrategy = 1 << (iota + 1)

	// LeastTasks always selects the pool with the least number of pending tasks.
	LeastTasks
)

type contextReleaser interface {
	ReleaseContext(ctx context.Context) error
}

func releasePools(ctx context.Context, pools []contextReleaser) error {
	errCh := make(chan error, len(pools))
	var wg errgroup.Group
	for i, pool := range pools {
		func(p contextReleaser, idx int) {
			wg.Go(func() error {
				err := p.ReleaseContext(ctx)
				if err != nil {
					err = fmt.Errorf("pool %d: %v", idx, err)
				}
				errCh <- err
				return err
			})
		}(pool, i)
	}

	_ = wg.Wait()

	var errStr strings.Builder
	for i := 0; i < len(pools); i++ {
		if err := <-errCh; err != nil {
			errStr.WriteString(err.Error())
			errStr.WriteString(" | ")
		}
	}

	if errStr.Len() == 0 {

View on GitHub (pinned to 107e376781)

Solutions

  1. Parse/inspect the wrapped cause: errors.Is on the returned error against ants.ErrPoolClosed or context.DeadlineExceeded to branch behavior.
  2. Ensure Release is called exactly once; guard MultiPool shutdown with sync.Once.
  3. Extend the context deadline (or use ReleaseTimeout with a larger duration) if drains time out.
  4. Log the pool index from the message to identify the misbehaving sub-pool.

Example fix

// before
if err := mp.ReleaseContext(ctx); err != nil {
    return err
}
// after
if err := mp.ReleaseContext(ctx); err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        log.Warn("multipool drain timeout (per-pool: %v)", err)
    } else if errors.Is(err, ants.ErrPoolClosed) {
        log.Debug("some sub-pools already closed")
    }
    return err
}
Defensive patterns

Strategy: try-catch

Try / catch

err := mp.ReleaseContext(ctx)
if err != nil {
    switch {
    case errors.Is(err, ants.ErrPoolClosed):
        // already released; ignore or debug-log
    case errors.Is(err, context.DeadlineExceeded):
        log.Warn("some sub-pools failed to drain: %v", err)
    default:
        return err
    }
}

Prevention

When it happens

Trigger: Calling multiPool.ReleaseContext(ctx) where a sub-pool returns an error from ReleaseContext — typically ErrPoolClosed on an already-released sub-pool, or context.DeadlineExceeded when workers do not drain in time; the error is wrapped per-pool as `pool <i>: <cause>`.

Common situations: Double-shutdown paths releasing a MultiPool twice; tight shutdown deadlines with long-running tasks causing per-pool release timeouts; mixed standalone pools and MultiPools where one was closed earlier.

Related errors


AI-assisted analysis of panjf2000/ants@107e376781 (2026-09-06). Data as JSON: /api/errors/be47fc796fa39ef1. Report an issue: GitHub.