{"record":{"id":"97997cbef7f25468","repo":"dotnet/aspnetcore","slug":"cannot-refresh-authentication-before-the-connectio","errorCode":null,"errorMessage":"Cannot refresh authentication before the connection is started.","messagePattern":"Cannot refresh authentication before the connection is started\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/SignalR/clients/ts/signalr/src/HttpConnection.ts","lineNumber":415,"sourceCode":"\n        refreshUrl.searchParams.append(\"id\", connectionToken);\n        return refreshUrl.toString();\n    }\n\n    private _configureAuthenticationRefresh(negotiateResponse: INegotiateResponse): void {\n        this._connectionToken = negotiateResponse.connectionToken;\n        this._connectionUrl = this.baseUrl;\n\n        const authenticationRefreshFeature: IAuthenticationRefreshFeature = {\n            initialTokenLifetimeInSeconds: this._initialTokenLifetimeInSeconds,\n            refreshAuthentication: () => this._refreshAuthentication(),\n        };\n        this.features.authenticationRefresh = authenticationRefreshFeature;\n    }\n\n    private async _refreshAuthentication(): Promise<number | undefined> {\n        if (!this._connectionToken || !this._connectionUrl) {\n            throw new Error(\"Cannot refresh authentication before the connection is started.\");\n        }\n\n        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);","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/SignalR/clients/ts/signalr/src/HttpConnection.ts#L397-L433","documentation":"_refreshAuthentication is invoked by the authenticationRefresh feature to renew the transport token. It requires an active connection (a stored _connectionToken and _connectionUrl). If either is missing — meaning _configureAuthenticationRefresh has not yet run — there is no connection context to refresh against, so it throws.","triggerScenarios":"The refreshAuthentication callback in connection.features.authenticationRefresh is invoked before startTransport completed and called _configureAuthenticationRefresh. Can also happen if the connection was torn down between scheduling and running the refresh.","commonSituations":"User code or a custom transport invokes features.authenticationRefresh.refreshAuthentication() manually before the connection is fully started. A race where the token lifetime timer fires during the brief window before _configureAuthenticationRefresh runs. A reconnection attempt that calls refresh on a not-yet-established generation.","solutions":["Do not call features.authenticationRefresh.refreshAuthentication() yourself; let the client invoke it after the connection is established.","Ensure connection.start() has fully resolved before any manual refresh attempt.","If refreshing from app code, guard on connection.state === 'Connected'."],"exampleFix":"// before\nawait connection.start();\n// manually triggering refresh too early\nawait connection.features.authenticationRefresh?.refreshAuthentication();\n\n// after — let the client drive refresh based on initialTokenLifetimeInSeconds\nawait connection.start();\n// refresh happens automatically; do not invoke manually","handlingStrategy":"validation","validationCode":"function canRefreshAuth(conn: signalR.HubConnection): boolean {\n  return conn.state === signalR.HubConnectionState.Connected\n    && !!conn.features.authenticationRefresh;\n}","typeGuard":"function hasAuthenticationRefresh(feat: unknown): feat is { refreshAuthentication: () => Promise<number | undefined> } {\n  return typeof feat === \"object\" && feat !== null\n    && typeof (feat as any).refreshAuthentication === \"function\";\n}","tryCatchPattern":"try {\n  await conn.features.authenticationRefresh?.refreshAuthentication();\n} catch (e) {\n  if (e instanceof Error && /Cannot refresh authentication before/.test(e.message)) {\n    // connection not started yet — wait for start() to resolve\n  }\n  throw e;\n}","preventionTips":["Never call refreshAuthentication manually; let the client schedule it.","Gate any manual refresh on connection.state === Connected.","Await start() before exercising connection.features."],"tags":["authentication","connection-state","lifecycle","token"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}