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

  1. Do not call features.authenticationRefresh.refreshAuthentication() yourself; let the client invoke it after the connection is established.
  2. Ensure connection.start() has fully resolved before any manual refresh attempt.
  3. 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

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

Related errors


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)