dotnet/aspnetcore · warning · AbortError

The connection was stopped during negotiation.

Error message

The connection was stopped during negotiation.

What it means

During the negotiate loop, after each /negotiate round-trip the client checks _connectionState. If the user called stop() (or the connection was otherwise torn down) while negotiation was in flight, the state is Disconnecting/Disconnected and the client throws AbortError to abandon startup cleanly.

Solutions

  1. Treat AbortError specially in your start() catch block and do not surface it as a real error.
  2. Avoid calling stop() until start() has resolved; gate stop() on the start promise.
  3. If you must support stop-during-start, swallow AbortError silently in the UI.

Example fix

// before
const startPromise = connection.start();
// user cancels immediately
connection.stop();
await startPromise; // throws AbortError

// after
const startPromise = connection.start();
await startPromise;
// only stop after start resolved
await connection.stop();

// or, in the catch:
try { await connection.start(); }
catch (e) {
  if (e instanceof signalR.AbortError) return; // expected on cancel
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

let startPromise: Promise<void> | null = null;
async function safeStart(conn: signalR.HubConnection) {
  startPromise = conn.start();
  await startPromise;
  startPromise = null;
}
async function safeStop(conn: signalR.HubConnection) {
  if (startPromise) await startPromise.catch(() => {});
  await conn.stop();
}

Type guard

import { AbortError } from "@microsoft/signalr";
function isAbortError(e: unknown): e is AbortError { return e instanceof AbortError; }

Try / catch

import { AbortError } from "@microsoft/signalr";
try { await connection.start(); }
catch (e) {
  if (e instanceof AbortError) return; // expected when stop() raced start()
  throw e;
}

Prevention

When it happens

Trigger: User calls connection.stop() (or start() was racing with a stop) while HttpConnection is still waiting on the negotiate HTTP response. Also possible if the underlying connection silently disconnected and the state machine flipped during the await.

Common situations: A 'cancel' button or route-change handler that calls stop() immediately after start() on a slow network. A component unmount in React that aborts the connection mid-handshake. Reactive code that starts and immediately stops the connection based on a flag.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/9e102d9e2daf8085. Report an issue: GitHub.

Appendix: source

Thrown at src/SignalR/clients/ts/signalr/src/HttpConnection.ts:272

            if (this._options.skipNegotiation) {
                if (this._options.transport === HttpTransportType.WebSockets) {
                    // No need to add a connection ID in this case
                    this.transport = this._constructTransport(HttpTransportType.WebSockets);
                    // We should just call connect directly in this case.
                    // No fallback or negotiate in this case.
                    await this._startTransport(url, transferFormat);
                } else {
                    throw new Error("Negotiation can only be skipped when using the WebSocket transport directly.");
                }
            } else {
                let negotiateResponse: INegotiateResponse | null = null;
                let redirects = 0;

                do {
                    negotiateResponse = await this._getNegotiationResponse(url);
                    // the user tries to stop the connection when it is being started
                    if (this._connectionState === ConnectionState.Disconnecting || this._connectionState === ConnectionState.Disconnected) {
                        throw new AbortError("The connection was stopped during negotiation.");
                    }

                    if (negotiateResponse.error) {
                        throw new Error(negotiateResponse.error);
                    }

                    if ((negotiateResponse as any).ProtocolVersion) {
                        throw new Error("Detected a connection attempt to an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details.");
                    }

                    if (negotiateResponse.url) {
                        url = negotiateResponse.url;
                    }

                    if (negotiateResponse.accessToken) {
                        // Replace the current access token factory with one that uses
                        // the returned access token
                        this._setTransportAccessToken(negotiateResponse.accessToken);

View on GitHub (pinned to 3600ca084e)