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
- Retry the request/startup once the competing instance finishes setup; the tenant will no longer be uninitialized.
- Check for and clear a stale lock (e.g. in the distributed lock store/database) if no other instance is running.
- Increase the lock timeout/acquisition options in AutoSetupLockOptions.
- 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
- Give the lock a generous timeout before scaling out
- Avoid launching all replicas simultaneously against an uninitialized tenant
- Monitor for stale locks after crashed deployments
- Use a shared distributed lock store (database/Redis) across all instances
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
- Couldn't acquire a lock to update the sitemap within
- Failed to acquire a lock before activating the tenant
- The ClamAV antivirus scanner timed out while scanning
- The ClamAV antivirus scanner is enabled but the connection…
- The ClamAV antivirus scanner is enabled but the transfer…
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)