dotnet/aspnetcore · error · Error
Cannot refresh authentication before the connection is…
Error message
Cannot refresh authentication before the connection is started.
What it means
_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.
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'.
Example fix
// before await connection.start(); // manually triggering refresh too early await connection.features.authenticationRefresh?.refreshAuthentication(); // after — let the client drive refresh based on initialTokenLifetimeInSeconds await connection.start(); // refresh happens automatically; do not invoke manually
Defensive patterns
Strategy: validation
Validate before calling
function canRefreshAuth(conn: signalR.HubConnection): boolean {
return conn.state === signalR.HubConnectionState.Connected
&& !!conn.features.authenticationRefresh;
} Type guard
function hasAuthenticationRefresh(feat: unknown): feat is { refreshAuthentication: () => Promise<number | undefined> } {
return typeof feat === "object" && feat !== null
&& typeof (feat as any).refreshAuthentication === "function";
} Try / catch
try {
await conn.features.authenticationRefresh?.refreshAuthentication();
} catch (e) {
if (e instanceof Error && /Cannot refresh authentication before/.test(e.message)) {
// connection not started yet — wait for start() to resolve
}
throw e;
} Prevention
- Never call refreshAuthentication manually; let the client schedule it.
- Gate any manual refresh on connection.state === Connected.
- Await start() before exercising connection.features.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Failed to retrieve token, no account found.
- Scopes not granted.
- The connection was stopped during negotiation.
- Unexpected status code returned from authentication refresh
- Authentication refresh is only supported with HTTP-based…
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/97997cbef7f25468.
Report an issue: GitHub.
Appendix: source
Thrown at src/SignalR/clients/ts/signalr/src/HttpConnection.ts:415
refreshUrl.searchParams.append("id", connectionToken);
return refreshUrl.toString();
}
private _configureAuthenticationRefresh(negotiateResponse: INegotiateResponse): void {
this._connectionToken = negotiateResponse.connectionToken;
this._connectionUrl = this.baseUrl;
const authenticationRefreshFeature: IAuthenticationRefreshFeature = {
initialTokenLifetimeInSeconds: this._initialTokenLifetimeInSeconds,
refreshAuthentication: () => this._refreshAuthentication(),
};
this.features.authenticationRefresh = authenticationRefreshFeature;
}
private async _refreshAuthentication(): Promise<number | undefined> {
if (!this._connectionToken || !this._connectionUrl) {
throw new Error("Cannot refresh authentication before the connection is started.");
}
const connectionGeneration = this._connectionGeneration;
const headers: {[k: string]: string} = {};
const [name, value] = getUserAgentHeader();
headers[name] = value;
const refreshUrl = this._createRefreshUrl(this._connectionUrl, this._connectionToken);
this._logger.log(LogLevel.Debug, `Sending authentication refresh request: ${refreshUrl}.`);
const request: HttpRequest = {
content: "",
headers: { ...headers, ...this._options.headers },
timeout: this._options.timeout,
withCredentials: this._options.withCredentials,
};
this._httpClient.markAuthenticationRefreshRequest(request);
const response = await this._httpClient.post(refreshUrl, request);View on GitHub (pinned to 3600ca084e)