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
- Treat this as an expected cancellation: catch AbortError and do not retry or log as failure.
- Guard teardown so stop() is only called when a start is intended to be cancelled, and await start() before issuing stop() where possible.
- Debounce or cancel component-level connect logic on unmount.
- 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
- Always await start() before calling stop() in teardown paths.
- Track a 'starting' flag so stop() during startup is intentional and handled.
- In component unmount handlers, catch and swallow AbortError.
- Never let a watchdog auto-stop a connection that is still negotiating without logging.
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
- HttpConnection.stopConnection
- The tenant ' ' can't be removed as it is neither…
- Cannot perform this operation because the shell scope for…
- The ' ' argument is required.
- The ' ' argument should not be empty.
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)