{"record":{"id":"1b264798d065af86","repo":"OrchardCMS/OrchardCore","slug":"failed-to-acquire-a-lock-before-activating-the-tenant","errorCode":null,"errorMessage":"Failed to acquire a lock before activating the tenant: {ShellContext.Settings.Name}","messagePattern":"Failed to acquire a lock before activating the tenant: (.+?)","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScope.cs","lineNumber":326,"sourceCode":"\n        if (_state.HasFlag(ShellScopeStates.ServiceScopeOnly))\n        {\n            return;\n        }\n\n        // Try to acquire a lock before using a new scope, so that a next process gets the last committed data.\n        (var locker, var locked) = await ShellContext.TryAcquireShellActivateLockAsync();\n        if (!locked)\n        {\n            // The retry logic increases the delay between 2 attempts (max of 10s), so if there are too\n            // many concurrent requests, one may experience a timeout while waiting before a new retry.\n            if (ShellContext.IsActivated)\n            {\n                // Don't throw if the shell is activated.\n                return;\n            }\n\n            throw new TimeoutException($\"Failed to acquire a lock before activating the tenant: {ShellContext.Settings.Name}\");\n        }\n\n        await using var acquiredLock = locker;\n\n        // The tenant gets activated here.\n        if (!ShellContext.IsActivated)\n        {\n            await new ShellScope(ShellContext).UsingAsync(async scope =>\n            {\n                var tenantEvents = scope.ServiceProvider.GetServices<IModularTenantEvents>();\n                foreach (var tenantEvent in tenantEvents)\n                {\n                    await tenantEvent.ActivatingAsync();\n                }\n\n                foreach (var tenantEvent in tenantEvents.Reverse())\n                {\n                    await tenantEvent.ActivatedAsync();","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/OrchardCMS/OrchardCore/blob/4306c0717fe573f6fca1b4955909ddab6a192807/src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScope.cs#L308-L344","documentation":"During shell activation (ActivateShellInternalAsync), the tenant must acquire its distributed lock before activation proceeds. If the lock could not be acquired within the timeout, a TimeoutException naming the tenant is thrown, signaling that another process (or a stuck prior run) holds the lock on this tenant.","triggerScenarios":"Starting/concurrently activating the same tenant from multiple hosts or threads while a database/file distributed lock is held; a crashed process leaving a stale lock row/file until expiry; long-running activation exceeding the lock timeout while activation is retried via UsingAsync.","commonSituations":"Multi-instance farms (web garden or scaled-out deployments) racing tenant startup; a previous deployment terminated mid-activation leaving locks in the database; slow database causing lock acquisition to exceed the timeout.","solutions":["Retry after the lock timeout expires once the current holder finishes activation","Check for stale locks in the lock store (database table/file) and clear entries from dead instances after confirming no holder is alive","Ensure only one activation flow runs per tenant at a time (avoid duplicate background activation tasks)","Verify the lock provider (database) is reachable and performant; slow DB lookups can push acquisition past the timeout"],"exampleFix":"// before\nawait shellHost.GetSettings(...); // races with another instance activating the same tenant\n// after\ntry\n{\n    await ShellScope.UsingAsync(shellHost, tenant);\n}\ncatch (TimeoutException) when (message.Contains(\"Failed to acquire a lock\"))\n{\n    await Task.Delay(TimeSpan.FromSeconds(5));\n    // retry activation\n}","handlingStrategy":"retry","validationCode":"// Detect an already-activated shell before forcing activation\nif (shellHost.TryGetShellContext(tenant, out var ctx) && ctx.IsActivated)\n    return; // no lock/activation needed","typeGuard":null,"tryCatchPattern":"try { await ShellScope.UsingAsync(shellHost, tenant); }\ncatch (TimeoutException ex) when (ex.Message.Contains(\"Failed to acquire a lock\"))\n{\n    await Task.Delay(TimeSpan.FromSeconds(10));\n    // retry with bounded attempts\n}","preventionTips":["Avoid multiple processes activating the same tenant concurrently","Monitor and clean stale locks after crashed deployments","Keep the lock store (database) healthy and responsive","Use single-flight/background activation rather than ad-hoc triggers"],"tags":["tenants","concurrency","lock","timeout"],"backgroundTag":"request-timeout","analyzedSha":"4306c0717fe573f6fca1b4955909ddab6a192807","analyzedAt":"2026-09-13T17:41:05.024Z","contentChangedAt":"2026-09-13T17:41:05.024Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}