OrchardCMS/OrchardCore · warning · AbortError

The connection was stopped during negotiation.

Error message

The connection was stopped during negotiation.

What it means

During startInternal's negotiate loop, the client re-checks its connection state after each negotiate HTTP call. If the user called stop() while negotiation was still in flight (state moved to Disconnecting or Disconnected), the client aborts startup with an AbortError carrying this message instead of continuing to build a transport on a dead connection.

Solutions

  1. Treat this as an expected cancellation: catch AbortError and do not retry or log as failure.
  2. Guard teardown so stop() is only called when a start is intended to be cancelled, and await start() before issuing stop() where possible.
  3. Debounce or cancel component-level connect logic on unmount.
  4. Increase the negotiate timeout / check server responsiveness if negotiate requests routinely hang long enough for stop() to interleave.

Example fix

// before
try {
  await connection.start();
} catch (err) {
  console.error(err); // noisy AbortError on unmount
}
// after
try {
  await connection.start();
} catch (err) {
  if (err instanceof signalR.AbortError || err.name === "AbortError") {
    return; // stop() was called during negotiation — expected
  }
  console.error(err);
}
Defensive patterns

Strategy: try-catch

Type guard

function isAbortError(err) {
  return err && (err.name === "AbortError" || err instanceof signalR.AbortError);
}

Try / catch

try {
  await connection.start();
} catch (err) {
  if (err.name === "AbortError") {
    return; // stop() raced with start() during negotiation — expected
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling hubConnection.stop() (or startInternal being cancelled) while the initial start()'s POST /hub/negotiate request is still pending; rapid start-then-stop sequences; component unmount tearing down the connection during page load.

Common situations: React/Vue/Angular components unmounting before SignalR finishes connecting; user navigating away during app boot; watchdog code that force-stops 'stuck' connections while the first negotiate request is slow.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/5c0ea58fd15fc97c. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore.Modules/OrchardCore.SignalR/wwwroot/Scripts/signalr.js:2844

                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 = 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 === "Disconnecting" /* ConnectionState.Disconnecting */ || this._connectionState === "Disconnected" /* ConnectionState.Disconnected */) {
                        throw new AbortError("The connection was stopped during negotiation.");
                    }
                    if (negotiateResponse.error) {
                        throw new Error(negotiateResponse.error);
                    }
                    if (negotiateResponse.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
                        const accessToken = negotiateResponse.accessToken;
                        this._accessTokenFactory = () => accessToken;
                        // set the factory to undefined so the AccessTokenHttpClient won't retry with the same token, since we know it won't change until a connection restart
                        this._httpClient._accessToken = accessToken;
                        this._httpClient._accessTokenFactory = undefined;

View on GitHub (pinned to 4306c0717f)