{"record":{"id":"7b7735c99ec5a3d4","repo":"dotnet/aspnetcore","slug":"unexpected-status-code-returned-from-authenticatio","errorCode":null,"errorMessage":"Unexpected status code returned from authentication refresh '${response.statusCode}'","messagePattern":"Unexpected status code returned from authentication refresh '(.+?)'","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/SignalR/clients/ts/signalr/src/HttpConnection.ts","lineNumber":436,"sourceCode":"        const connectionGeneration = this._connectionGeneration;\n        const headers: {[k: string]: string} = {};\n        const [name, value] = getUserAgentHeader();\n        headers[name] = value;\n\n        const refreshUrl = this._createRefreshUrl(this._connectionUrl, this._connectionToken);\n        this._logger.log(LogLevel.Debug, `Sending authentication refresh request: ${refreshUrl}.`);\n\n        const request: HttpRequest = {\n            content: \"\",\n            headers: { ...headers, ...this._options.headers },\n            timeout: this._options.timeout,\n            withCredentials: this._options.withCredentials,\n        };\n        this._httpClient.markAuthenticationRefreshRequest(request);\n        const response = await this._httpClient.post(refreshUrl, request);\n\n        if (response.statusCode !== 200) {\n            throw new Error(`Unexpected status code returned from authentication refresh '${response.statusCode}'`);\n        }\n\n        if (typeof response.content !== \"string\") {\n            throw new Error(\"Invalid authentication refresh response received: expected JSON content.\");\n        }\n\n        if (connectionGeneration !== this._connectionGeneration) {\n            return undefined;\n        }\n\n        const refreshResponse = JSON.parse(response.content) as { accessToken?: unknown, tokenLifetimeSeconds?: unknown };\n        if (typeof refreshResponse.accessToken === \"string\" && refreshResponse.accessToken) {\n            // Redirecting servers can return a transport token that should replace the current cached token.\n            this._setTransportAccessToken(refreshResponse.accessToken);\n        } else if (!this._transportAccessTokenFromServer) {\n            // Without a server-provided transport token, reuse the app token that successfully authenticated refresh.\n            const refreshRequestToken = this._httpClient.getRefreshRequestToken(response);\n            if (refreshRequestToken) {","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/SignalR/clients/ts/signalr/src/HttpConnection.ts#L418-L454","documentation":"Thrown at HttpConnection.ts:436 when the POST to the `/refresh` endpoint returns any HTTP status other than 200. The refresh protocol expects a 200 with a JSON body containing accessToken and tokenLifetimeSeconds; any other status is treated as a failed refresh and the new token is not applied.","triggerScenarios":"The auth refresh token has fully expired server-side (401); the connection token is unknown to the server (404); a server error (500); a proxy returning 403/502; the refresh endpoint not being implemented on the server (404) when using an older server version.","commonSituations":"Server clock skew causing the refresh to arrive after expiration; reverse proxy stripping the `id=` query param; ASP.NET Core server too old to implement the refresh endpoint (added in a later version); app token revoked out-of-band.","solutions":["Verify the server is running a version of ASP.NET Core SignalR that implements the authentication refresh endpoint.","Check server logs for the failing refresh request to see the real status code and reason.","Ensure the connection token (`id=` query param) is preserved by any proxy.","Handle the thrown error in your refreshAuthentication catch and trigger a full reconnect with a fresh app token instead of relying on refresh."],"exampleFix":"// before\nawait hub.refreshAuthentication();\n\n// after\ntry {\n  await hub.refreshAuthentication();\n} catch (e) {\n  console.warn('refresh failed, full reconnect', e);\n  await hub.stop();\n  // re-acquire app token, then start again\n  await hub.start();\n}","handlingStrategy":"try-catch","validationCode":"// cannot fully pre-check (status depends on server), but you can verify\n// the server implements the refresh endpoint with a HEAD/GET probe.","typeGuard":null,"tryCatchPattern":"try { await hub.refreshAuthentication(); }\ncatch (e) {\n  const m = /status code returned from authentication refresh '(\\d+)'/.exec(String(e));\n  if (m) {\n    const code = Number(m[1]);\n    if (code === 401 || code === 403) {\n      // app token fully expired - full reconnect with fresh token\n      await hub.stop(); await hub.start();\n    } else if (code === 404) {\n      throw new Error('server does not implement the refresh endpoint');\n    }\n  }\n  throw e;\n}","preventionTips":["Run a server version that implements the refresh endpoint.","Ensure proxies preserve the id= query param on refresh requests.","Schedule refreshes early enough (refreshBeforeExpirationInMilliseconds) to avoid post-expiration 401s."],"tags":["authentication","refresh","network","server-compatibility"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}