{"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/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/HubConnection.java#L159-L195","documentation":"Thrown as HttpRequestException by handleNegotiate when the HTTP POST to the resolved negotiate URL returns any status code other than 200. The negotiate step is the first server contact during start() and determines available transports, connection tokens, and redirect URLs, so any non-200 is fatal. The %d and %s placeholders carry the numeric status code and reason phrase for diagnosis.","triggerScenarios":"connection.start() triggers a POST to {baseUrl}/negotiate and the server responds 401/403 (auth), 404 (wrong endpoint or missing SignalR mapping), 500 (server error), 502/503 (reverse proxy/gateway failure), or any other non-200. This surfaces as an onError on the start() Completable.","commonSituations":"Authentication token missing/expired (401/403). The base URL points to the wrong path or SignalR hub is not registered server-side (404). A reverse proxy (nginx, IIS ARR) in front strips or mishandles the negotiate subrequest (502/504). CORS or cross-origin restrictions at the negotiate stage. Server-side rate limiting returning 429. ASP.NET Core SignalR not configured or wrong version mismatch.","solutions":["Inspect the status code in the error message; for 401/403 supply or refresh the access token via .withAccessTokenProvider(...).","For 404, verify the base URL and that the hub is mapped server-side (e.g. MapHub<T> in ASP.NET Core) and that the negotiate endpoint is reachable.","For 5xx, check the reverse proxy/gateway configuration to ensure negotiate POST requests are forwarded correctly.","Reproduce the negotiate POST manually (e.g. curl -X POST <url>/negotiate) with the same headers to see the raw server response."],"exampleFix":"// before\nHubConnection conn = HubConnectionBuilder.create(\"https://example.com/hub\").build();\nconn.start().blockingAwait(); // 401: no auth token\n\n// after\nHubConnection conn = HubConnectionBuilder.create(\"https://example.com/hub\")\n    .withAccessTokenProvider(Single.just(() -> getJwtToken()))\n    .build();\nconn.start().blockingAwait();","handlingStrategy":"try-catch","validationCode":"// Pre-flight: hit the negotiate endpoint with the same headers to detect auth/config issues.\n// (Illustrative; use your HTTP client.)\nint status = httpPost(negotiateUrl, headers).getStatusCode();\nif (status != 200) {\n    throw new IllegalStateException(\"Negotiate pre-flight failed: \" + status);\n}","typeGuard":null,"tryCatchPattern":"connection.start()\n    .subscribe(() -> { /* connected */ },\n        error -> {\n            if (error instanceof HttpRequestException) {\n                int code = ((HttpRequestException) error).getStatusCode();\n                // handle 401/403 -> refresh token; 404 -> fix URL; 5xx -> retry/backoff\n            }\n        });","preventionTips":["Use .withAccessTokenProvider(Single.defer(() -> Single.just(refreshToken()))) so tokens refresh on each connect.","Verify the hub endpoint and negotiate path are reachable (curl the negotiate URL in CI).","Configure reverse proxies (nginx/IIS) to forward negotiate and WebSocket upgrade correctly.","Instrument start() failures by status code to distinguish auth vs routing vs server errors."],"tags":["signalr","java","network","negotiate","authentication","http"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}