dotnet/aspnetcore · error · Error
Authentication refreshBeforeExpirationInMilliseconds must be
Error message
Authentication refreshBeforeExpirationInMilliseconds must be a finite number greater than or equal to 0.
What it means
Thrown at HubConnection.ts:1033 by _validateAuthenticationRefreshOptions, called from the HubConnection constructor (line 140). It validates the `refreshBeforeExpirationInMilliseconds` field of IAuthenticationRefreshOptions: it must be either undefined or a finite number >= 0. NaN, Infinity, negative numbers, or non-number types are rejected at construction time.
Source
Thrown at src/SignalR/clients/ts/signalr/src/HubConnection.ts:1033
retryReason,
});
} catch (e) {
this._logger.log(LogLevel.Error, `IRetryPolicy.nextRetryDelayInMilliseconds(${previousRetryCount}, ${elapsedMilliseconds}) threw error '${e}'.`);
return null;
}
}
private _validateAuthenticationRefreshOptions(): void {
if (!this._authenticationRefreshOptions) {
return;
}
const refreshBeforeExpirationInMilliseconds = this._authenticationRefreshOptions.refreshBeforeExpirationInMilliseconds;
if (refreshBeforeExpirationInMilliseconds !== undefined &&
(typeof refreshBeforeExpirationInMilliseconds !== "number" ||
!Number.isFinite(refreshBeforeExpirationInMilliseconds) ||
refreshBeforeExpirationInMilliseconds < 0)) {
throw new Error("Authentication refreshBeforeExpirationInMilliseconds must be a finite number greater than or equal to 0.");
}
}
private _scheduleAuthenticationRefreshIfNeeded(): void {
if (!this._isAutoAuthenticationRefreshEnabled()) {
return;
}
const authenticationRefreshFeature = this.connection.features.authenticationRefresh as IAuthenticationRefreshFeature | undefined;
const initialTokenLifetimeInSeconds = authenticationRefreshFeature?.initialTokenLifetimeInSeconds;
if (isValidAuthenticationTokenLifetime(initialTokenLifetimeInSeconds)) {
this._scheduleAuthenticationRefresh(initialTokenLifetimeInSeconds);
}
}
private _isAutoAuthenticationRefreshEnabled(): boolean {
return !!this._authenticationRefreshOptions && this._authenticationRefreshOptions.enableAutoRefresh !== false;
}View on GitHub (pinned to 294cab2f9b)
Solutions
- Pass a positive finite number in milliseconds: `withAuthenticationRefresh({ refreshBeforeExpirationInMilliseconds: 5 * 60 * 1000 })`.
- If loading from env, parse and validate: `const n = Number(process.env.REFRESH_MS); if (!Number.isFinite(n) || n < 0) throw ...`.
- Omit the field to use the default of 5 minutes.
- Ensure you pass milliseconds, not seconds.
Example fix
// before
new HubConnectionBuilder()
.withUrl(url)
.withAuthenticationRefresh({ refreshBeforeExpirationInMilliseconds: '300000' })
.build(); // throws
// after
new HubConnectionBuilder()
.withUrl(url)
.withAuthenticationRefresh({ refreshBeforeExpirationInMilliseconds: 5 * 60 * 1000 })
.build(); Defensive patterns
Strategy: validation
Validate before calling
function validateRefreshOpts(opts) {
const v = opts?.refreshBeforeExpirationInMilliseconds;
if (v !== undefined && (typeof v !== 'number' || !Number.isFinite(v) || v < 0)) {
throw new Error('refreshBeforeExpirationInMilliseconds must be a finite number >= 0');
}
}
validateRefreshOpts(refreshOptions);
new HubConnectionBuilder().withUrl(url).withAuthenticationRefresh(refreshOptions).build(); Type guard
function isValidRefreshBeforeMs(v: unknown): v is number | undefined {
return v === undefined || (typeof v === 'number' && Number.isFinite(v) && v >= 0);
} Try / catch
// thrown synchronously from build(); wrap construction
try { builder.withAuthenticationRefresh(opts).build(); }
catch (e) {
if (/refreshBeforeExpirationInMilliseconds/.test(String(e))) {
opts.refreshBeforeExpirationInMilliseconds = 5 * 60 * 1000;
return builder.withAuthenticationRefresh(opts).build();
}
throw e;
} Prevention
- Always specify the value in milliseconds.
- Coerce env/JSON values to Number and validate with Number.isFinite before passing.
- Omit the field to accept the 5-minute default.
When it happens
Trigger: Building a HubConnection via `HubConnectionBuilder.withAuthenticationRefresh({ refreshBeforeExpirationInMilliseconds: <bad> }).build()` where the value is negative, NaN, Infinity, a string, or null. The constructor runs the validation and throws before the connection is returned.
Common situations: Passing a value parsed from JSON/env as a string (e.g. "300000"); a math expression that produced NaN; setting Infinity expecting 'never expire'; sign error producing a negative; passing a value in seconds instead of milliseconds and ending up with a tiny or negative number after subtraction in user code.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- withCredentials option was not a 'boolean' or 'undefined' va
- Cannot refresh authentication before the connection is start
- Cannot refresh authentication when the connection is not act
- A valid url is required.
- EqualTo validator requires a non-empty "other" parameter.
AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06).
Data as JSON: /api/errors/827bec7b9f3dd6c9.
Report an issue: GitHub.