ppy/osu · error · TimeoutException

Could not obtain a lock to disconnect. A previous attempt is

Error message

Could not obtain a lock to disconnect. A previous attempt is likely stuck.

What it means

Thrown as TimeoutException by PersistentEndpointClientConnector.disconnect when takeLock is true and connectionLock cannot be acquired within 10 seconds. Same semaphore as connect; a stuck prior operation blocks the disconnect.

Source

Thrown at osu.Game/Online/PersistentEndpointClientConnector.cs:188

                await handleErrorAndDelay(ex, CancellationToken.None).ConfigureAwait(false);
            else
                Logger.Log($"{ClientName} disconnected", LoggingTarget.Network);

            // make sure a disconnect wasn't triggered (and this is still the active connection).
            if (!hasBeenCancelled)
                await Task.Run(connect, CancellationToken.None).ConfigureAwait(false);
        }

        protected Task Disconnect() => disconnect(true);

        private async Task disconnect(bool takeLock)
        {
            cancelExistingConnect();

            if (takeLock)
            {
                if (!await connectionLock.WaitAsync(10000).ConfigureAwait(false))
                    throw new TimeoutException("Could not obtain a lock to disconnect. A previous attempt is likely stuck.");
            }

            try
            {
                if (CurrentConnection != null)
                    await CurrentConnection.DisposeAsync().ConfigureAwait(false);
            }
            finally
            {
                isConnected.Value = false;
                CurrentConnection = null;

                if (takeLock)
                    connectionLock.Release();
            }
        }

        private void cancelExistingConnect()

View on GitHub (pinned to d9c73e12ad)

Solutions

  1. Coordinate connect/disconnect so they don't run simultaneously (the connector's state machine already attempts this; respect it).
  2. Diagnose the task holding connectionLock (await hang inside the locked region).
  3. Ensure CurrentConnection.DisposeAsync completes promptly or has its own timeout.
Defensive patterns

Strategy: try-catch

Validate before calling

// cannot pre-validate an async lock; serialize disconnect with connect via the connector state machine.

Try / catch

try { await connector.Disconnect(); }
catch (TimeoutException) { /* prior op holds the lock: investigate, do not hammer */ }

Prevention

When it happens

Trigger: Calling Disconnect() (which calls disconnect(true)) while a connect() is mid-flight and holding connectionLock, or a prior disconnect never released it.

Common situations: Disconnect issued concurrently with a connect attempt; a hung connection disposal keeping the lock; shutdown path racing a reconnect.

Related errors


AI-assisted analysis of ppy/osu@d9c73e12ad (2026-08-13). Data as JSON: /api/errors/1069fa5fc33fb18b. Report an issue: GitHub.