microsoft/garnet · error · Exception

Unable to call ThreadPool.SetMaxThreads with {maxThreads}, {

Error message

Unable to call ThreadPool.SetMaxThreads with {maxThreads}, {maxCPThreads}

What it means

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.

Source

Thrown at libs/host/GarnetServer.cs:241

                maxThreads = opts.ThreadPoolMaxThreads;
                maxChanged = true;
            }
            if (opts.ThreadPoolMaxIOCompletionThreads > 0)
            {
                maxCPThreads = opts.ThreadPoolMaxIOCompletionThreads;
                maxChanged = true;
            }

            // First try to set the max threads
            var setMax = !maxChanged || ThreadPool.SetMaxThreads(maxThreads, maxCPThreads);

            // Set the min threads
            if (minChanged && !ThreadPool.SetMinThreads(minThreads, minCPThreads))
                throw new Exception($"Unable to call ThreadPool.SetMinThreads with {minThreads}, {minCPThreads}");

            // Retry to set max threads if it wasn't set in the earlier step
            if (!setMax && !ThreadPool.SetMaxThreads(maxThreads, maxCPThreads))
                throw new Exception($"Unable to call ThreadPool.SetMaxThreads with {maxThreads}, {maxCPThreads}");

            opts.Initialize(loggerFactory);
            StoreWrapper.DatabaseCreatorDelegate createDatabaseDelegate = (int dbId) =>
                CreateDatabase(dbId, opts, clusterFactory, customCommandManager);

            if (!opts.DisablePubSub)
                subscribeBroker = new SubscribeBroker(null, opts.PubSubPageSizeBytes(), pubSubEpoch, startFresh: true, logger);

            logger?.LogTrace("TLS is {tlsEnabled}", opts.TlsOptions == null ? "disabled" : "enabled");

            // Create Garnet TCP server if none was provided.
            if (servers == null)
            {
                servers = new IGarnetServer[opts.EndPoints.Length];
                for (var i = 0; i < servers.Length; i++)
                {
                    if (opts.EndPoints[i] is UnixDomainSocketEndPoint)
                    {

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Ensure ThreadPoolMaxThreads >= ThreadPoolMinThreads and ThreadPoolMaxIOCompletionThreads >= ThreadPoolMinIOCompletionThreads.
  2. Increase max thread values or decrease min thread values so the constraint is satisfied.
  3. If running in containers, verify the host allows the requested thread counts.
  4. Consider not overriding thread pool settings and letting the runtime self-tune.

Example fix

// before
var opts = new GarnetServerOptions
{
    ThreadPoolMinThreads = 200,
    ThreadPoolMaxThreads = 100  // max < min is invalid
};

// after
var opts = new GarnetServerOptions
{
    ThreadPoolMinThreads = 100,
    ThreadPoolMaxThreads = 200
};
Defensive patterns

Strategy: validation

Validate before calling

if (opts.ThreadPoolMaxThreads > 0 || opts.ThreadPoolMaxIOCompletionThreads > 0)
{
    ThreadPool.GetMinThreads(out var minWorker, out var minIO);
    if (opts.ThreadPoolMaxThreads > 0 && opts.ThreadPoolMaxThreads < minWorker)
        throw new InvalidOperationException($"Max worker threads ({opts.ThreadPoolMaxThreads}) is below min ({minWorker})");
    if (opts.ThreadPoolMaxIOCompletionThreads > 0 && opts.ThreadPoolMaxIOCompletionThreads < minIO)
        throw new InvalidOperationException($"Max IO threads ({opts.ThreadPoolMaxIOCompletionThreads}) is below min ({minIO})");
}

Type guard

static bool AreMaxThreadSettingsValid(GarnetServerOptions opts)
{
    ThreadPool.GetMinThreads(out var minWorker, out var minIO);
    return (opts.ThreadPoolMaxThreads <= 0 || opts.ThreadPoolMaxThreads >= minWorker)
        && (opts.ThreadPoolMaxIOCompletionThreads <= 0 || opts.ThreadPoolMaxIOCompletionThreads >= minIO);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13). Data as JSON: /api/errors/da2b39ee0a45e46d. Report an issue: GitHub.