{"record":{"id":"748b741377db8773","repo":"dotnet/aspnetcore","slug":"unexpected-status-code-returned-from-negotiate-d","errorCode":null,"errorMessage":"Unexpected status code returned from negotiate: %d %s.","messagePattern":"Unexpected status code returned from negotiate: (.+?) (.+?)\\.","errorType":"http","errorClass":"HttpRequestException","httpStatus":null,"severity":"error","filePath":"src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/HubConnection.java","lineNumber":177,"sourceCode":"            this.handshakeResponseTimeout = handshakeResponseTimeout;\n        }\n\n        this.headers = headers;\n        this.skipNegotiate = skipNegotiate;\n\n        this.serverTimeout = serverTimeout;\n        this.keepAliveInterval = keepAliveInterval;\n\n        this.callback = (payload) -> ReceiveLoop(payload);\n    }\n\n    private Single<NegotiateResponse> handleNegotiate(String url, Map<String, String> localHeaders) {\n        HttpRequest request = new HttpRequest();\n        request.addHeaders(localHeaders);\n\n        return httpClient.post(Negotiate.resolveNegotiateUrl(url, this.negotiateVersion), request).map((response) -> {\n            if (response.getStatusCode() != 200) {\n                throw new HttpRequestException(String.format(\"Unexpected status code returned from negotiate: %d %s.\",\n                        response.getStatusCode(), response.getStatusText()), response.getStatusCode());\n            }\n            JsonReader reader = new JsonReader(new StringReader(new String(response.getContent().array(), StandardCharsets.UTF_8)));\n            NegotiateResponse negotiateResponse = new NegotiateResponse(reader);\n\n            if (negotiateResponse.getError() != null) {\n                throw new RuntimeException(negotiateResponse.getError());\n            }\n\n            if (negotiateResponse.getAccessToken() != null) {\n                localHeaders.put(\"Authorization\", \"Bearer \" + negotiateResponse.getAccessToken());\n            }\n\n            return negotiateResponse;\n        });\n    }\n\n    /**","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/HubConnection.java#L159-L195","documentation":"Thrown inside handleNegotiate when the POST to the /negotiate endpoint returns an HTTP status other than 200. Wrapped as HttpRequestException carrying the status code and status text. The negotiate request is the first thing the client sends during start(); a non-200 here means the server rejected the negotiate handshake before any transport was chosen.","triggerScenarios":"httpClient.post(resolveNegotiateUrl(url)) returns a response whose getStatusCode() != 200 - e.g. 401/403 (auth), 404 (wrong endpoint/path), 500 (server crash), 502/503 (gateway), 426 (protocol upgrade required).","commonSituations":"Hub URL is wrong (points to a non-hub path, 404); auth token missing/expired (401/403); server/App Service down or misconfigured (500/502/503); CORS/hosting issue blocks negotiate; the endpoint requires a specific negotiate version and rejects v1; reverse proxy not forwarding the negotiate POST.","solutions":["Read the status code and text from the exception: 401/403 -> fix the access token provider; 404 -> correct the hub URL/path; 5xx -> server-side investigation.","Confirm the URL ends at the hub endpoint and that the server maps that route to a hub.","Verify the access token provider returns a valid token before each start.","Check the server logs for the matching negotiate request; ensure the reverse proxy forwards POST /negotiate."],"exampleFix":"// before\nHubConnection conn = HubConnectionBuilder.create(\"http://srv/api\") // wrong path -> 404\n  .build();\n\n// after\nHubConnection conn = HubConnectionBuilder.create(\"http://srv/hubs/chat\") // correct hub route\n  .withAccessTokenProvider(Single.just(token))\n  .build();","handlingStrategy":"try-catch","validationCode":"// Validate reachability and auth before start:\n// 1) GET {hub} returns 200/401 (not 404) - confirms the route exists.\n// 2) Access token provider returns a non-empty token when auth is required.\nString url = \"http://srv/hubs/chat\";\n","typeGuard":"boolean isLikelyHubEndpoint(int statusCode) {\n  return statusCode == 200 || statusCode == 401 || statusCode == 403;\n}","tryCatchPattern":"try {\n  conn.start().blockingAwait();\n} catch (HttpRequestException e) {\n  int code = e.getStatusCode(); // or read from the message\n  // 401/403 -> fix token provider; 404 -> fix URL; 5xx -> server logs\n}","preventionTips":["Confirm the hub URL maps to a real hub route.","Ensure the access token provider returns a valid token before start().","Smoke-test POST {hub}/negotiate with curl and the same headers."],"tags":["signalr","java","network","negotiate","http"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}