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
- Inspect the negotiate response's availableTransports field to see what the server actually advertises.
- Enable WebSockets (and/or LongPolling) on the ASP.NET Core server (ConfigureKestrel / AddSignalR options).
- Ensure the reverse proxy passes through WebSockets so the server includes it in availableTransports.
- 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
- Ensure the ASP.NET Core server enables at least WebSockets or LongPolling.
- Configure the reverse proxy to pass WebSocket upgrades so the server advertises WebSockets.
- Verify the base URL targets a real SignalR hub, not a generic endpoint.
- Log availableTransports from the negotiate response for diagnostics.
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
- Negotiation can only be skipped when using the WebSocket tra
- Negotiation can only be skipped when using the WebSocket tra
- Message is incomplete.
- Unexpected status code returned from negotiate: %d %s.
- HubConnection trying to negotiate when not in the CONNECTING
AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06).
Data as JSON: /api/errors/66c6120675ffcf1f.
Report an issue: GitHub.