microsoft/aspire · error · InvalidOperationException
Already connected to
Error message
Already connected to {Name} backchannel. What it means
ConnectAsync on ExtensionBackchannel tracks connection state via _rpcTaskCompletionSource. If the RPC task is already completed (a connection exists), calling ConnectAsync again throws this InvalidOperationException — the backchannel is designed for a single connection to the extension, not repeated connects.
Solutions
- Do not call ConnectAsync if already connected; check the connected state (or wrap in try/catch for InvalidOperationException) before connecting.
- Call ConnectAsync once during initialization and rely on subsequent helper methods to reuse the existing connection.
- If a reconnection is genuinely needed, dispose/reset the backchannel instance before connecting again.
Example fix
// before
await backchannel.ConnectAsync(endpoint, cancellationToken);
await backchannel.ConnectAsync(endpoint, cancellationToken); // throws
// after
if (Volatile.Read(ref backchannel._connected) == 0)
{
await backchannel.ConnectAsync(endpoint, cancellationToken);
} Defensive patterns
Strategy: type-guard
Validate before calling
if (backchannel.IsConnected) return; // skip redundant ConnectAsync
Type guard
bool shouldConnect = !backchannel.IsConnected;
Try / catch
try { await backchannel.ConnectAsync(endpoint, ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Already connected"))
{ /* ignore: connection already established */ } Prevention
- Call ConnectAsync exactly once during initialization.
- Guard helper-method usage with a connected check before forcing a connect.
- Centralize connection establishment in one place instead of per-call connects.
When it happens
Trigger: Calling ConnectAsync (directly or indirectly through DisplayMessageAsync, DisplaySuccessAsync, DisplaySubtleMessageAsync, DisplayErrorAsync, DisplayEmptyLineAsync, or DisplayIncompatibleVersionErrorAsync) when the backchannel has already completed a successful connection.
Common situations: Calling multiple display/prompt helper methods when the first one already connected, or application code that explicitly calls ConnectAsync after auto-connect already ran during a helper call.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Cannot reconnect: no previous connection.
- Already connected to AppHost backchannel.
- AppHost is incompatible with the CLI. The AppHost must be…
- Not connected to auxiliary backchannel.
- Resource update buffering was not enabled for this watcher.
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/075bf3fe31f0b94f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Cli/Backchannel/ExtensionBackchannel.cs:252
}
return;
async Task ConnectCoreAsync()
{
if (_connectCoreAsyncOverride is not null)
{
await _connectCoreAsyncOverride(cancellationToken).ConfigureAwait(false);
return;
}
try
{
using var activity = _activitySource.StartActivity();
if (_rpcTaskCompletionSource.Task.IsCompleted)
{
throw new InvalidOperationException($"Already connected to {Name} backchannel.");
}
_logger.LogDebug("Connecting to {Name} backchannel at {SocketPath}", Name, endpoint);
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var addressParts = endpoint.Split(':');
if (addressParts.Length != 2 || !int.TryParse(addressParts[1], out var port) || port <= 0 ||
port > 65535)
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ErrorStrings.InvalidSocketPath, endpoint));
}
await socket.ConnectAsync(addressParts[0], port, cancellationToken);
_logger.LogDebug("Connected to {Name} backchannel at {SocketPath}", Name, endpoint);
var stream = new SslStream(new NetworkStream(socket, true),
leaveInnerStreamOpen: true,
userCertificateValidationCallback: (_, c, _, e) =>
{View on GitHub (pinned to 25830f84bd)