{"record":{"id":"d5cdf1d6f3dcec13","repo":"ppy/osu","slug":"could-not-obtain-a-lock-to-connect-a-previous-att","errorCode":null,"errorMessage":"Could not obtain a lock to connect. A previous attempt is likely stuck.","messagePattern":"Could not obtain a lock to connect\\. A previous attempt is likely stuck\\.","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"osu.Game/Online/PersistentEndpointClientConnector.cs","lineNumber":92,"sourceCode":"                case APIState.Offline:\r\n                    await disconnect(true).ConfigureAwait(true);\r\n                    break;\r\n\r\n                case APIState.Online:\r\n                case APIState.RequiresSecondFactorAuth:\r\n                    await connect().ConfigureAwait(true);\r\n                    break;\r\n            }\r\n        }\r\n\r\n        private async Task connect()\r\n        {\r\n            cancelExistingConnect();\r\n            // reset retry delay to default.\r\n            retryDelay = 3000;\r\n\r\n            if (!await connectionLock.WaitAsync(10000).ConfigureAwait(false))\r\n                throw new TimeoutException(\"Could not obtain a lock to connect. A previous attempt is likely stuck.\");\r\n\r\n            try\r\n            {\r\n                while (apiState.Value == APIState.RequiresSecondFactorAuth || apiState.Value == APIState.Online)\r\n                {\r\n                    // ensure any previous connection was disposed.\r\n                    // this will also create a new cancellation token source.\r\n                    await disconnect(false).ConfigureAwait(false);\r\n\r\n                    // this token will be valid for the scope of this connection.\r\n                    // if cancelled, we can be sure that a disconnect or reconnect is handled elsewhere.\r\n                    var cancellationToken = connectCancelSource.Token;\r\n\r\n                    cancellationToken.ThrowIfCancellationRequested();\r\n\r\n                    Logger.Log($\"{ClientName} connecting...\", LoggingTarget.Network);\r\n\r\n                    try\r","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/ppy/osu/blob/d9c73e12adff2feaae4a3e158d36fe5883faf6ca/osu.Game/Online/PersistentEndpointClientConnector.cs#L74-L110","documentation":"Thrown as TimeoutException by PersistentEndpointClientConnector.connect when it cannot acquire connectionLock within 10 seconds. The semaphore serializes connect/disconnect; a held lock means a previous connect or disconnect is stuck and never released.","triggerScenarios":"connectionLock.WaitAsync(10000) returns false in connect(). A prior connect()/disconnect() is still running (deadlock, hung await, or an unobserved exception that skipped the lock release).","commonSituations":"Rapid reconnect/disconnect churn racing on the lock; an await inside the locked region never completing (e.g. DisposeAsync on the connection hanging); a bug where the finally releasing the lock was missed.","solutions":["Investigate the previous connect/disconnect that holds the lock (capture a hang dump of the task holding connectionLock).","Avoid issuing concurrent Connect/Disconnect calls; serialize them through the connector's API.","Ensure the underlying IConnection.DisposeAsync cannot hang (add its own timeout)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// cannot pre-validate an async lock; avoid concurrent Connect/Disconnect calls through the connector.","typeGuard":null,"tryCatchPattern":"try { await connector.Connect(); }\ncatch (TimeoutException) { /* previous connect stuck: report, do not immediately retry unchanged */ }","preventionTips":["Do not call Connect while a Disconnect is in flight.","Ensure IConnection.DisposeAsync cannot hang.","On this timeout, capture diagnostics rather than retrying blindly."],"tags":["network","connector","deadlock","timeout","concurrency"],"backgroundTag":null,"analyzedSha":"d9c73e12adff2feaae4a3e158d36fe5883faf6ca","analyzedAt":"2026-08-13T14:12:54.015Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}