OrchardCMS/OrchardCore · error · Error

HttpConnection.stopConnection

Error message

HttpConnection.stopConnection(${error}) was called while the connection is still in the connecting state.

What it means

HttpConnection.stopConnection is an internal lifecycle method that must only run on a connection that is connected/disconnecting/disconnected. If it is invoked while the connection is still in the Connecting state, the client throws this Error because that indicates a transport calling onclose during startup — an internal state-machine invariant violation.

Solutions

  1. Await connection.start() before calling connection.stop(), and serialize start/stop calls (e.g. a mutex/promise chain).
  2. Catch the error around start() and retry with backoff instead of calling stop() concurrently.
  3. Check negotiate endpoint reachability/auth — a failing handshake often triggers the transport onclose during Connecting.
  4. Upgrade @microsoft/signalr — several early transport-close races were fixed in later versions.

Example fix

// before
connection.stop();
await connection.start();
// after
await connection.stop();
await connection.start();
// or guard:
// if (connection.state === signalR.HubConnectionState.Disconnected) await connection.start();
Defensive patterns

Strategy: try-catch

Validate before calling

if (connection.state !== signalR.HubConnectionState.Disconnected) {
  throw new Error('Cannot start: connection is not in Disconnected state');
}

Type guard

const canStart = (c) => c.state === signalR.HubConnectionState.Disconnected;

Try / catch

try {
  if (connection.state === signalR.HubConnectionState.Disconnected) {
    await connection.start();
  }
} catch (e) {
  if (String(e.message).includes('stopConnection') || String(e.message).includes('connecting state')) {
    // wait for settle, then retry with backoff
  }
}

Prevention

When it happens

Trigger: A transport's onclose fires during the start() sequence (e.g. WebSocket fails immediately, SSE errors during connection) before stopConnectionMoveTo/transition to Connected completes; race between start() and stop(); manual call to internal stopConnection API while connecting.

Common situations: Rapid connection.start()/stop() calls racing each other; network drops exactly during handshake; buggy custom transport; server rejecting the connection mid-handshake (auth failure, 401/404 negotiate).

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


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

Appendix: source

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

            }
        }
    }
    _isITransport(transport) {
        return transport && typeof (transport) === "object" && "connect" in transport;
    }
    _stopConnection(error) {
        this._logger.log(LogLevel.Debug, `HttpConnection.stopConnection(${error}) called while in state ${this._connectionState}.`);
        this.transport = undefined;
        // If we have a stopError, it takes precedence over the error from the transport
        error = this._stopError || error;
        this._stopError = undefined;
        if (this._connectionState === "Disconnected" /* ConnectionState.Disconnected */) {
            this._logger.log(LogLevel.Debug, `Call to HttpConnection.stopConnection(${error}) was ignored because the connection is already in the disconnected state.`);
            return;
        }
        if (this._connectionState === "Connecting" /* ConnectionState.Connecting */) {
            this._logger.log(LogLevel.Warning, `Call to HttpConnection.stopConnection(${error}) was ignored because the connection is still in the connecting state.`);
            throw new Error(`HttpConnection.stopConnection(${error}) was called while the connection is still in the connecting state.`);
        }
        if (this._connectionState === "Disconnecting" /* ConnectionState.Disconnecting */) {
            // A call to stop() induced this call to stopConnection and needs to be completed.
            // Any stop() awaiters will be scheduled to continue after the onclose callback fires.
            this._stopPromiseResolver();
        }
        if (error) {
            this._logger.log(LogLevel.Error, `Connection disconnected with error '${error}'.`);
        }
        else {
            this._logger.log(LogLevel.Information, "Connection disconnected.");
        }
        if (this._sendQueue) {
            this._sendQueue.stop().catch((e) => {
                this._logger.log(LogLevel.Error, `TransportSendQueue.stop() threw error '${e}'.`);
            });
            this._sendQueue = undefined;
        }

View on GitHub (pinned to 4306c0717f)