{"record":{"id":"5bd6042fc9202aea","repo":"elsa-workflows/elsa-core","slug":"could-not-acquire-distributed-lock-with-key-lockkey-within","errorCode":null,"errorMessage":"Could not acquire distributed lock with key '{lockKey}' within the configured timeout of {distributedLockingOptions.Value.LockAcquisitionTimeout}.","messagePattern":"Could not acquire distributed lock with key '(.+?)' within the configured timeout of (.+?)\\.","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Workflows.Runtime/Services/WorkflowResumer.cs","lineNumber":132,"sourceCode":"                try\n                {\n                    var response = await workflowClient.RunInstanceAsync(runRequest, cancellationToken);\n                    logger.LogDebug(\"Resumed workflow instance {WorkflowInstanceId} with bookmark {BookmarkId}\", bookmark.WorkflowInstanceId, bookmark.Id);\n                    responses.Add(response);\n                }\n                catch (WorkflowInstanceNotFoundException)\n                {\n                    // The workflow instance does not (yet) exist in the DB.\n                    logger.LogDebug(\"No workflow instance with ID {WorkflowInstanceId} found for bookmark {BookmarkId} at this time.\", bookmark.WorkflowInstanceId, bookmark.Id);\n                }\n            }\n\n            return responses;\n        }\n        catch (TimeoutException e)\n        {\n            // Rethrow but with a more specific message.\n            throw new TimeoutException($\"Could not acquire distributed lock with key '{lockKey}' within the configured timeout of {distributedLockingOptions.Value.LockAcquisitionTimeout}.\", e);\n        }\n    }\n}","sourceCodeStart":114,"sourceCodeEnd":135,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Workflows.Runtime/Services/WorkflowResumer.cs#L114-L135","documentation":"WorkflowResumer.ResumeAsync wraps the entire bookmark-resume operation in a distributed lock named 'workflow-resumer:{filter}'. If the lock cannot be acquired within DistributedLockingOptions.LockAcquisitionTimeout, the underlying Medallion TimeoutException is rethrown with this message naming the lock key and configured timeout. This prevents concurrent resume of the same bookmarks by multiple workers.","triggerScenarios":"Calling any IWorkflowResumer.ResumeAsync overload (by bookmark id, by TActivity + stimulus, by BookmarkFilter, or via ResumeBookmarkRequest) while another process/thread holds the lock 'workflow-resumer:<hashable filter string>' for longer than the configured LockAcquisitionTimeout.","commonSituations":"Multiple Elsa workers contending for the same event-driven bookmark; a long-running resume that exceeds the timeout; a stuck/crashed holder leaving the lock until expiry; LockAcquisitionTimeout configured too low for slow environments (e.g. database latency).","solutions":["Increase DistributedLockingOptions.LockAcquisitionTimeout in app configuration to cover worst-case lock hold time.","Check which process/thread holds the lock and why it is slow (long-running workflow resume, DB contention); fix that bottleneck.","Ensure a shared, correctly configured distributed lock provider (e.g. SQL Server/Postgres/Redis) is registered; a misconfigured backend can behave like a permanently held lock.","Retry the resume after the timeout with backoff, ideally with idempotent handling since another worker may have already resumed the bookmark."],"exampleFix":"// before\nservices.Configure<DistributedLockingOptions>(o => o.LockAcquisitionTimeout = TimeSpan.FromSeconds(1));\n// after\nservices.Configure<DistributedLockingOptions>(o => o.LockAcquisitionTimeout = TimeSpan.FromSeconds(30));","handlingStrategy":"retry","validationCode":"var opts = serviceProvider.GetRequiredService<IOptions<DistributedLockingOptions>>().Value;\nif (opts.LockAcquisitionTimeout < TimeSpan.FromSeconds(5)) throw new InvalidOperationException(\"LockAcquisitionTimeout too low for resume operations\");","typeGuard":null,"tryCatchPattern":"try { await resumer.ResumeAsync(filter, options, ct); }\ncatch (TimeoutException) { logger.LogWarning(\"Resume lock '{Key}' timed out; will retry\", lockKey); await Task.Delay(TimeSpan.FromSeconds(5), ct); /* retry or surface */ }","preventionTips":["Size LockAcquisitionTimeout above worst-case resume duration.","Use a robust distributed lock backend shared by all workers.","Monitor lock contention metrics and alert on frequent timeouts.","Make resume handling idempotent so safe retries are possible."],"tags":["distributed-lock","timeout","concurrency","workflow-runtime"],"backgroundTag":"request-timeout","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}