{"record":{"id":"da2b39ee0a45e46d","repo":"microsoft/garnet","slug":"unable-to-call-threadpool-setmaxthreads-with-maxt","errorCode":null,"errorMessage":"Unable to call ThreadPool.SetMaxThreads with {maxThreads}, {maxCPThreads}","messagePattern":"Unable to call ThreadPool\\.SetMaxThreads with (.+?), (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"libs/host/GarnetServer.cs","lineNumber":241,"sourceCode":"                maxThreads = opts.ThreadPoolMaxThreads;\n                maxChanged = true;\n            }\n            if (opts.ThreadPoolMaxIOCompletionThreads > 0)\n            {\n                maxCPThreads = opts.ThreadPoolMaxIOCompletionThreads;\n                maxChanged = true;\n            }\n\n            // First try to set the max threads\n            var setMax = !maxChanged || ThreadPool.SetMaxThreads(maxThreads, maxCPThreads);\n\n            // Set the min threads\n            if (minChanged && !ThreadPool.SetMinThreads(minThreads, minCPThreads))\n                throw new Exception($\"Unable to call ThreadPool.SetMinThreads with {minThreads}, {minCPThreads}\");\n\n            // Retry to set max threads if it wasn't set in the earlier step\n            if (!setMax && !ThreadPool.SetMaxThreads(maxThreads, maxCPThreads))\n                throw new Exception($\"Unable to call ThreadPool.SetMaxThreads with {maxThreads}, {maxCPThreads}\");\n\n            opts.Initialize(loggerFactory);\n            StoreWrapper.DatabaseCreatorDelegate createDatabaseDelegate = (int dbId) =>\n                CreateDatabase(dbId, opts, clusterFactory, customCommandManager);\n\n            if (!opts.DisablePubSub)\n                subscribeBroker = new SubscribeBroker(null, opts.PubSubPageSizeBytes(), pubSubEpoch, startFresh: true, logger);\n\n            logger?.LogTrace(\"TLS is {tlsEnabled}\", opts.TlsOptions == null ? \"disabled\" : \"enabled\");\n\n            // Create Garnet TCP server if none was provided.\n            if (servers == null)\n            {\n                servers = new IGarnetServer[opts.EndPoints.Length];\n                for (var i = 0; i < servers.Length; i++)\n                {\n                    if (opts.EndPoints[i] is UnixDomainSocketEndPoint)\n                    {","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/host/GarnetServer.cs#L223-L259","documentation":"Thrown by GarnetServer.InitializeServer when ThreadPool.SetMaxThreads returns false on the retry attempt. The code first tries to set max threads, then sets min threads, then retries max threads if the first attempt didn't succeed (because the CLR requires min <= max). If this final retry also fails, the server cannot proceed.","triggerScenarios":"Setting ThreadPoolMaxThreads or ThreadPoolMaxIOCompletionThreads to values the CLR rejects — typically values below the current min thread counts, zero, or values exceeding system limits. The retry at line 240 fires when the first SetMaxThreads at line 233 also failed.","commonSituations":"Setting max threads lower than min threads in the same config; container CPU/memory constraints that limit max threads; OS-level thread pool caps; configuring max threads to values below the .NET runtime minimum.","solutions":["Ensure ThreadPoolMaxThreads >= ThreadPoolMinThreads and ThreadPoolMaxIOCompletionThreads >= ThreadPoolMinIOCompletionThreads.","Increase max thread values or decrease min thread values so the constraint is satisfied.","If running in containers, verify the host allows the requested thread counts.","Consider not overriding thread pool settings and letting the runtime self-tune."],"exampleFix":"// before\nvar opts = new GarnetServerOptions\n{\n    ThreadPoolMinThreads = 200,\n    ThreadPoolMaxThreads = 100  // max < min is invalid\n};\n\n// after\nvar opts = new GarnetServerOptions\n{\n    ThreadPoolMinThreads = 100,\n    ThreadPoolMaxThreads = 200\n};","handlingStrategy":"validation","validationCode":"if (opts.ThreadPoolMaxThreads > 0 || opts.ThreadPoolMaxIOCompletionThreads > 0)\n{\n    ThreadPool.GetMinThreads(out var minWorker, out var minIO);\n    if (opts.ThreadPoolMaxThreads > 0 && opts.ThreadPoolMaxThreads < minWorker)\n        throw new InvalidOperationException($\"Max worker threads ({opts.ThreadPoolMaxThreads}) is below min ({minWorker})\");\n    if (opts.ThreadPoolMaxIOCompletionThreads > 0 && opts.ThreadPoolMaxIOCompletionThreads < minIO)\n        throw new InvalidOperationException($\"Max IO threads ({opts.ThreadPoolMaxIOCompletionThreads}) is below min ({minIO})\");\n}","typeGuard":"static bool AreMaxThreadSettingsValid(GarnetServerOptions opts)\n{\n    ThreadPool.GetMinThreads(out var minWorker, out var minIO);\n    return (opts.ThreadPoolMaxThreads <= 0 || opts.ThreadPoolMaxThreads >= minWorker)\n        && (opts.ThreadPoolMaxIOCompletionThreads <= 0 || opts.ThreadPoolMaxIOCompletionThreads >= minIO);\n}","tryCatchPattern":null,"preventionTips":["Ensure max thread values are always >= min thread values.","Set min and max thread values together as a consistent pair.","Let the runtime self-tune the thread pool unless you have profiling data."],"tags":["thread-pool","startup","configuration","resource-limits"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}