microsoft/garnet · error · ObjectDisposedException

GarnetClientSession

Error message

GarnetClientSession

What it means

ReconnectAsync in GarnetClientSession guards against reuse of a disposed session by checking the Disposed property and throwing ObjectDisposedException named 'GarnetClientSession'. Before reconnecting it attempts to clean up the prior networkSender/networkHandler/socket (swallowing errors) and then calls ConnectAsync. This guard means: once Disposed is true, the session is permanently dead and cannot be resurrected.

Source

Thrown at libs/client/ClientSession/GarnetClientSession.cs:399

                    await socket.ConnectAsync(endpoint, cancellationToken).ConfigureAwait(false);
                }
            }
            catch (Exception ex)
            {
                logger?.LogWarning(ex, "Failed at GarnetClient.TryConnectSocketAsync");
                socket.Dispose();
                return false;
            }

            return true;
        }

        /// <summary>
        /// Reconnect to server
        /// </summary>
        public Task ReconnectAsync(int timeoutMs = 0, CancellationToken token = default)
        {
            if (Disposed) throw new ObjectDisposedException("GarnetClientSession");
            try
            {
                networkSender?.ReturnResponseObject();
                socket?.Dispose();
                networkHandler?.Dispose();
            }
            catch { }
            return ConnectAsync(timeoutMs, token);
        }

        /// <summary>
        /// Dispose instance
        /// </summary>
        public void Dispose()
        {
            if (Interlocked.Increment(ref disposed) > 1) return;

            networkSender?.ReturnResponseObject();

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Track lifecycle explicitly: never call ReconnectAsync after Dispose; cancel any pending reconnect task before disposing.
  2. Create a fresh GarnetClientSession instead of reconnecting a disposed one — Reconnect is for live sessions, not resurrected ones.
  3. Guard the reconnect call site with a disposed flag or ObjectDisposedException handling.

Example fix

// before
session.ReconnectAsync();

// after — create a new session if the old one is disposed
if (session.Disposed)
    session = new GarnetClientSession(endpoint, ...);
await session.ConnectAsync();
Defensive patterns

Strategy: validation

Validate before calling

if (session is null) throw new ArgumentNullException(nameof(session));
if (session.Disposed) throw new ObjectDisposedException(nameof(GarnetClientSession));

Type guard

if (session?.Disposed ?? true) { /* create a new session instead */ }

Try / catch

try { await session.ReconnectAsync(); }
catch (ObjectDisposedException) { session = new GarnetClientSession(endpoint, ...); await session.ConnectAsync(); }

Prevention

When it happens

Trigger: Calling ReconnectAsync on a GarnetClientSession after Dispose() (or DisposeAsync) has already run; a retry/reconnect loop that runs after the owning pool or application has torn the session down.

Common situations: A reconnect-on-failure policy that outlives the application shutdown; disposing in a finally block but an outstanding reconnect task still fires; using a session from an AsyncPool that was disposed while a reconnect was queued.

Related errors


AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13). Data as JSON: /api/errors/e06b33c6078e7cfc. Report an issue: GitHub.