dotnet/aspnetcore · error · RuntimeException
${negotiateResponse.getError()}
Error message
${negotiateResponse.getError()} What it means
Thrown inside handleNegotiate when a 200 negotiate response carries a non-null error string in its JSON body. The server answered the negotiate POST successfully (HTTP 200) but populated the 'error' field, indicating it could not complete negotiation - typically because the negotiate version is unsupported or the connection is unauthorized at the app layer.
Source
Thrown at src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/HubConnection.java:184
this.keepAliveInterval = keepAliveInterval;
this.callback = (payload) -> ReceiveLoop(payload);
}
private Single<NegotiateResponse> handleNegotiate(String url, Map<String, String> localHeaders) {
HttpRequest request = new HttpRequest();
request.addHeaders(localHeaders);
return httpClient.post(Negotiate.resolveNegotiateUrl(url, this.negotiateVersion), request).map((response) -> {
if (response.getStatusCode() != 200) {
throw new HttpRequestException(String.format("Unexpected status code returned from negotiate: %d %s.",
response.getStatusCode(), response.getStatusText()), response.getStatusCode());
}
JsonReader reader = new JsonReader(new StringReader(new String(response.getContent().array(), StandardCharsets.UTF_8)));
NegotiateResponse negotiateResponse = new NegotiateResponse(reader);
if (negotiateResponse.getError() != null) {
throw new RuntimeException(negotiateResponse.getError());
}
if (negotiateResponse.getAccessToken() != null) {
localHeaders.put("Authorization", "Bearer " + negotiateResponse.getAccessToken());
}
return negotiateResponse;
});
}
/**
* Indicates the state of the {@link HubConnection} to the server.
*
* @return HubConnection state enum.
*/
public HubConnectionState getConnectionState() {
return this.state.getHubConnectionState();
}View on GitHub (pinned to 3600ca084e)
Solutions
- Surface negotiateResponse.getError() to logs - it is the server's own explanation and the fastest path to root cause.
- Confirm client and server negotiate versions are compatible (the client hardcodes negotiateVersion = 1; ensure the server supports it).
- Check server-side auth/authorization on the hub endpoint and that the connection carries required claims.
- Reproduce with curl: POST {hub}/negotiate with the same headers and inspect the JSON error field.
Example fix
// before: client forces negotiateVersion the server rejects
// server replies 200 { "error": "Unsupported negotiate version." }
// after: align versions - update the server (ASP.NET Core) or client to a matching release,
// and verify the hub endpoint allows the caller. Defensive patterns
Strategy: try-catch
Validate before calling
// No client validation prevents a server-side error payload. // Before start(), confirm client/server negotiateVersion compatibility from your deployment matrix. boolean versionsCompatible = true; // from compatibility matrix
Type guard
// Server-supplied error string cannot be type-guarded client-side. // Capture and inspect NegotiateResponse.getError() in logs.
Try / catch
try {
conn.start().blockingAwait();
} catch (RuntimeException e) {
String msg = e.getMessage();
if (msg != null && !msg.contains("status code") && !msg.contains("negotiate")) {
// likely the server's negotiate error string - log and reconcile version/auth
} else throw e;
} Prevention
- Keep client negotiateVersion compatible with the server (this client uses v1).
- Check hub endpoint auth/authorization policies.
- Reproduce POST {hub}/negotiate with curl and inspect the JSON error field.
When it happens
Trigger: NegotiateResponse.getError() returns a value after the 200 check passed. The server's /negotiate returned 200 with an error JSON field; the client rethrows that error verbatim as a RuntimeException.
Common situations: Client and server negotiateVersion mismatch (client sends v1, server only supports v0 or vice versa); the server rejected the connection at the application layer (auth policy, IUserIdProvider); a transient server-side error during negotiate payload construction.
Related errors
- Unexpected status code returned from negotiate: %d %s.
- Negotiate redirection limit exceeded.
- There were no compatible transports on the server.
- Error reading NegotiateResponse
- Negotiation can only be skipped when using the WebSocket tra
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/4cb6f1725e4469d0.
Report an issue: GitHub.