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

  1. Surface negotiateResponse.getError() to logs - it is the server's own explanation and the fastest path to root cause.
  2. Confirm client and server negotiate versions are compatible (the client hardcodes negotiateVersion = 1; ensure the server supports it).
  3. Check server-side auth/authorization on the hub endpoint and that the connection carries required claims.
  4. 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

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


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/4cb6f1725e4469d0. Report an issue: GitHub.