dotnet/aspnetcore · error · RuntimeException

There were no compatible transports on the server.

Error message

There were no compatible transports on the server.

What it means

A RuntimeException thrown by startNegotiate when the client requested TransportEnum.ALL but the server's availableTransports list contains neither 'WebSockets' nor 'LongPolling'. With ALL, the client prefers WebSockets then falls back to LongPolling; if neither is advertised the connection cannot be established.

Source

Thrown at src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/HubConnection.java:385

    private Single<NegotiateResponse> startNegotiate(String url, int negotiateAttempts, Map<String, String> localHeaders) {
        if (this.state.getHubConnectionState() != HubConnectionState.CONNECTING) {
            throw new RuntimeException("HubConnection trying to negotiate when not in the CONNECTING state.");
        }

        return handleNegotiate(url, localHeaders).flatMap(response -> {
            if (response.getRedirectUrl() != null && negotiateAttempts >= MAX_NEGOTIATE_ATTEMPTS) {
                throw new RuntimeException("Negotiate redirection limit exceeded.");
            }

            if (response.getRedirectUrl() == null) {
                Set<String> transports = response.getAvailableTransports();
                if (this.transportEnum == TransportEnum.ALL) {
                    if (transports.contains("WebSockets")) {
                        response.setChosenTransport(TransportEnum.WEBSOCKETS);
                    } else if (transports.contains("LongPolling")) {
                        response.setChosenTransport(TransportEnum.LONG_POLLING);
                    } else {
                        throw new RuntimeException("There were no compatible transports on the server.");
                    }
                } else if (this.transportEnum == TransportEnum.WEBSOCKETS && !transports.contains("WebSockets") ||
                        (this.transportEnum == TransportEnum.LONG_POLLING && !transports.contains("LongPolling"))) {
                    throw new RuntimeException("There were no compatible transports on the server.");
                } else {
                    response.setChosenTransport(this.transportEnum);
                }

                String connectionToken = "";
                if (response.getVersion() > 0) {
                    this.state.getConnectionState().connectionId = response.getConnectionId();
                    connectionToken = response.getConnectionToken();
                } else {
                    connectionToken = response.getConnectionId();
                    this.state.getConnectionState().connectionId = connectionToken;
                }

                String finalUrl = Utils.appendQueryString(url, "id=" + connectionToken);

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Inspect the negotiate response's availableTransports field to see what the server actually advertises.
  2. Enable WebSockets (and/or LongPolling) on the ASP.NET Core server (ConfigureKestrel / AddSignalR options).
  3. Ensure the reverse proxy passes through WebSockets so the server includes it in availableTransports.
  4. Verify the base URL points to a real SignalR hub endpoint, not a generic JSON endpoint.

Example fix

// server (ASP.NET Core): ensure WebSockets are available
// before: only SSE enabled server-side
// after: enable WebSockets in Kestrel + IIS/nginx
services.AddSignalR();
// program.cs / kestrel: ConfigureKestrel(o => o.AllowSynchronousIO = true) and enable WebSockets middleware
app.UseWebSockets();
Defensive patterns

Strategy: validation

Validate before calling

// Probe negotiate to see available transports before committing to start().
NegotiateResponse r = probeNegotiate(url);
Set<String> t = r.getAvailableTransports();
if (!t.contains("WebSockets") && !t.contains("LongPolling")) {
    throw new IllegalStateException("No compatible transports: " + t);
}

Try / catch

connection.start().subscribe(onComplete, error -> {
    if (error.getMessage().contains("no compatible transports")) {
        // check server config: enable WebSockets/LongPolling, fix proxy
    }
});

Prevention

When it happens

Trigger: During start(), after negotiate returns a non-redirect response, the client inspects response.getAvailableTransports(). If the set lacks both 'WebSockets' and 'LongPolling' and transportEnum == ALL, this error is thrown.

Common situations: Server only advertises Server-Sent Events or another transport the Java client doesn't implement. ASP.NET Core server configured to disable WebSockets and LongPolling (e.g. only WebSockets enabled but the server is behind a proxy that strips it from the list). Version/protocol mismatch where transport names differ. A non-SignalR endpoint returning a JSON body that parses as a negotiate response but lists no recognizable transports.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/66c6120675ffcf1f. Report an issue: GitHub.