OrchardCMS/OrchardCore · error · TimeoutException

Fails to acquire an auto setup lock for the tenant

Error message

Fails to acquire an auto setup lock for the tenant: {_setupOptions.ShellName}

What it means

AutoSetupMiddleware wraps tenant installation in a distributed lock so only one instance performs setup in a multi-instance farm. If TryAcquireAutoSetupLockAsync fails to obtain the lock within the configured timeout, it throws TimeoutException naming the tenant shell. This guarantees atomic setup when several nodes boot simultaneously.

Solutions

  1. Retry the request/startup once the competing instance finishes setup; the tenant will no longer be uninitialized.
  2. Check for and clear a stale lock (e.g. in the distributed lock store/database) if no other instance is running.
  3. Increase the lock timeout/acquisition options in AutoSetupLockOptions.
  4. Ensure all instances share the same database connection string so the lock is actually distributed.

Example fix

// before
services.Configure<AutoSetupLockOptions>(o => o.Timeout = TimeSpan.FromSeconds(5));
// after
services.Configure<AutoSetupLockOptions>(o => o.Timeout = TimeSpan.FromMinutes(2));
Defensive patterns

Strategy: retry

Validate before calling

// Before booting another instance, confirm no other holder:
// SELECT * FROM DistributedLock WHERE Name = 'AUTO_SETUP_<tenant>' AND Expires < now -- expect empty

Try / catch

try { await next.Invoke(context); }
catch (TimeoutException ex) when (ex.Message.Contains("auto setup lock"))
{
    logger.LogWarning(ex, "Auto setup lock contention for tenant; another node is setting it up");
    await Task.Delay(TimeSpan.FromSeconds(10));
    // retry or return 503
}

Prevention

When it happens

Trigger: Starting multiple application instances against the same uninitialized tenant at once; a stale lock row held by a crashed or slow instance; lock acquisition/maintenance timeouts set too low in AutoSetupLockOptions.

Common situations: Kubernetes/Docker scale-out where replicas race to set up the Default tenant; a previous failed setup left the lock un-released; database latency makes setup exceed the lock timeout.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/874341169b4c0db9. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore.Modules/OrchardCore.AutoSetup/AutoSetupMiddleware.cs:101

    /// <summary>
    /// The auto setup middleware invoke.
    /// </summary>
    /// <param name="httpContext">
    /// The http context.
    /// </param>
    /// <returns>
    /// The <see cref="Task"/>.
    /// </returns>
    public async Task InvokeAsync(HttpContext httpContext)
    {
        if (_setupOptions is not null && _shellSettings.IsUninitialized())
        {
            // Try to acquire a lock before starting installation, it guaranties an atomic setup in multi instances environment.
            (var locker, var locked) = await _distributedLock.TryAcquireAutoSetupLockAsync(_lockOptions);
            if (!locked)
            {
                throw new TimeoutException($"Fails to acquire an auto setup lock for the tenant: {_setupOptions.ShellName}");
            }

            await using var acquiredLock = locker;

            if (_shellSettings.IsUninitialized())
            {
                var pathBase = httpContext.Request.PathBase;
                if (!pathBase.HasValue)
                {
                    pathBase = "/";
                }

                // Check if the tenant was installed by another instance.
                using var settings = await _shellSettingsManager.LoadSettingsAsync(_shellSettings.Name);

                if (settings != null)
                {
                    settings.AsDisposable();

View on GitHub (pinned to 4306c0717f)