{"record":{"id":"be47fc796fa39ef1","repo":"panjf2000/ants","slug":"pool-d-v","errorCode":null,"errorMessage":"pool %d: %v","messagePattern":"pool (.+?): (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"multipool.go","lineNumber":60,"sourceCode":"\tRoundRobin LoadBalancingStrategy = 1 << (iota + 1)\n\n\t// LeastTasks always selects the pool with the least number of pending tasks.\n\tLeastTasks\n)\n\ntype contextReleaser interface {\n\tReleaseContext(ctx context.Context) error\n}\n\nfunc releasePools(ctx context.Context, pools []contextReleaser) error {\n\terrCh := make(chan error, len(pools))\n\tvar wg errgroup.Group\n\tfor i, pool := range pools {\n\t\tfunc(p contextReleaser, idx int) {\n\t\t\twg.Go(func() error {\n\t\t\t\terr := p.ReleaseContext(ctx)\n\t\t\t\tif err != nil {\n\t\t\t\t\terr = fmt.Errorf(\"pool %d: %v\", idx, err)\n\t\t\t\t}\n\t\t\t\terrCh <- err\n\t\t\t\treturn err\n\t\t\t})\n\t\t}(pool, i)\n\t}\n\n\t_ = wg.Wait()\n\n\tvar errStr strings.Builder\n\tfor i := 0; i < len(pools); i++ {\n\t\tif err := <-errCh; err != nil {\n\t\t\terrStr.WriteString(err.Error())\n\t\t\terrStr.WriteString(\" | \")\n\t\t}\n\t}\n\n\tif errStr.Len() == 0 {","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/panjf2000/ants/blob/107e37678122b00e1b365636ce4f80623ba41579/multipool.go#L42-L78","documentation":"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).","triggerScenarios":"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>`.","commonSituations":"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.","solutions":["Parse/inspect the wrapped cause: errors.Is on the returned error against ants.ErrPoolClosed or context.DeadlineExceeded to branch behavior.","Ensure Release is called exactly once; guard MultiPool shutdown with sync.Once.","Extend the context deadline (or use ReleaseTimeout with a larger duration) if drains time out.","Log the pool index from the message to identify the misbehaving sub-pool."],"exampleFix":"// before\nif err := mp.ReleaseContext(ctx); err != nil {\n    return err\n}\n// after\nif err := mp.ReleaseContext(ctx); err != nil {\n    if errors.Is(err, context.DeadlineExceeded) {\n        log.Warn(\"multipool drain timeout (per-pool: %v)\", err)\n    } else if errors.Is(err, ants.ErrPoolClosed) {\n        log.Debug(\"some sub-pools already closed\")\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := mp.ReleaseContext(ctx)\nif err != nil {\n    switch {\n    case errors.Is(err, ants.ErrPoolClosed):\n        // already released; ignore or debug-log\n    case errors.Is(err, context.DeadlineExceeded):\n        log.Warn(\"some sub-pools failed to drain: %v\", err)\n    default:\n        return err\n    }\n}","preventionTips":["Release the MultiPool exactly once (sync.Once).","Give the release context enough time for the slowest sub-pool's tasks.","Parse the `pool %d:` prefix to locate the failing sub-pool when debugging."],"tags":["go","error-wrapping","shutdown","multipool"],"backgroundTag":"upstream-api-error","analyzedSha":"107e37678122b00e1b365636ce4f80623ba41579","analyzedAt":"2026-09-06T13:28:28.492Z","contentChangedAt":"2026-09-06T13:28:28.492Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}